Search code examples
iosswiftxmlswxmlhash

Any easier way to convert XML to Plist?


I am seeking help with my process of converting XML to Swift dictionary then to a Plist.

TL;DR I am using SWXMLHash to parse an XML to Swift dictionary then to Plist so that users can edit data in an easier way. Parsing works fine but I was not able to find an easy way to convert XMLIndexer object to a dictionary and the way I am doing makes me think that there should be an easier/possibly more sophisticated way to do this.

Details:

Here is a sample XML that is very similar to what I am working on:

 let xmlSample1 = """
<?xml version="1.0" encoding="UTF-8"?>

<root name        = "basic"
        enabled     = "true"
        schema_ver  = "1"
        ver  = “14.0.2”
>

  <initial>
    <actions>

      <list name=“xxxx”> 1234 </list>

      <anotherList name = “xxxx”>
    1234
      </anotherList>


      <config items = "1" max_active = "1">
        <primary=“A B C D” />
      </config>

    </actions>
  </initial>
</root>

And this is how I am parsing it:

 override func viewDidLoad() {
        super.viewDidLoad()
        do {
            let parsedObject: Item = try SWXMLHash.parse(xmSample1)["root"].value()
            convertToDict(parsedObject: parsedObject)
        } catch let error {
            print("error occured:  ---> \(error)")
        }
    }

This is my model for parsing:

struct Item: XMLIndexerDeserializable {
    let name: String
    let enabled: String
    let schema_ver: String
    let ver: String
    let actions: Actions

    static func deserialize(_ node: XMLIndexer) throws -> Item {
        return try Item(
            name: node.value(ofAttribute: "name"),
            enabled: node.value(ofAttribute: "enabled"),
            schema_ver: node.value(ofAttribute: "schema_ver"),
            policy_ver: node.value(ofAttribute: "ver"),
            actions: node["initial"]["actions"].value()
        )
    }
}

and this is how I am converting to a Swift dictionary:

func convertToDict(parsedObject: Item) {
        let dict = [
            ItemKey.name : parsedObject.name,
            ItemKey.enabled : parsedObject.enabled,
            ItemKey.schema_ver : parsedObject.schema_ver,
            ItemKey.ver : parsedObject.ver,
            ] as [String : Any]

        do {
            try PropertyListEncoder().encode(dict).write(to: fileURL)
            print("saved to plist successfully")
        } catch {
            print(error)
        }
    }

The real XML is bigger than the sample thus makes it more tedious to convert to a dictionary. Is there a better way to do this or a ready solution that I missed?

Thanks for the help.


Solution

  • You could make your Item conform to Encodable

      struct Item: XMLIndexerDeserializable, Encodable {
        let name: String
        let enabled: String
        let schema_ver: String
        let ver: String
        let actions: Actions    // this property would have to be Encodable as well
      }
    
      struct Actions: Encodable {...}
    

    Then you don't have to use a dictionary, pretty much same as you had it.

    func convertToPlist(item: Item) {
          let encoder = PropertyListEncoder()
          encoder.outputFormat = .binary
          do {
              let data = try encoder.encode(item)
              let fileURL = URL(<where you want to save plist>)
              try data.write(to: fileURL)
          } catch {
              // Handle error
              print(error)
          }
    }
    

    Not sure on making the output smaller. PropertyListEncoder has a few different output options. .binary might decrease the size a bit.

    encoder.outputFormat = .binary