Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Codable and XMLParser in Swift4

Tags:

Using the new Swift4 Codable protocol works well for JSON-decoding (as explained here or here or in many other contributions). However, as it comes to XML-parsing, I couldn't find any information on whether this Codable protocol could also be used for XML decoding.

I tried to use the XMLParser (as can be seen in code excerpts below). But I failed to used the "Codable" protocol to simplify the XML-parsing process. How would I have to use the Codable protocol exactly to simplify XML-parsing ??

// the Fetching of the XML-data (excerpt shown here with a simple dataTask) :  let myTask = session.dataTask(with: myRequest) { (data, response, error) in                  // check for error     guard error == nil else {         completionHandler(nil, error!)         return     }     // make sure we got data in the response     guard let responseData = data else {        let error = XMLFetchError.objectSerialization(reason: "No data in response")         completionHandler(nil, error)         return     }                  // the responseData is XML !!!!!!!!!!!!!!     let parser = XMLParser(data: responseData)     parser.delegate = self     parser.parse() } myTask.resume() 

The corresponding XMLParserDelegate-methods:

func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {          self.resultTrip = elementName          // print(elementName)     if (self.resultTrip == "TripResult") {         self.resultTime = ""     }      }  func parser(_ parser: XMLParser, foundCharacters string: String) {          let data = string.trimmingCharacters(in: .whitespacesAndNewlines)          if data.count != 0 {                  switch self.resultTrip {         case "TimetabledTime": self.resultTime = data         default: break         }     } }  func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {          if self.resultTrip == "TripResult" {                  // HERE IS THE MISSING BIT: HOW DO YOU USE A CODABLE struct ???         var myTrip = TripResult(from: <#Decoder#>)         myTrip.resultID = self.resultTrip              }          print(resultTime) } 

The struct :

struct TripResult : Codable {     let resultId : String?     let trip : Trip?      enum CodingKeys: String, CodingKey {          case resultId = "ResultId"         case trip     }      init(from decoder: Decoder) throws {         let values = try decoder.container(keyedBy: CodingKeys.self)         resultId = try values.decodeIfPresent(String.self, forKey: .resultId)         trip = try Trip(from: decoder)     } } 

How would I have to use the Codable struct? Is there any nice example of how to use the Codable protocol for XML parsing?

like image 202
iKK Avatar asked Dec 06 '17 22:12

iKK


People also ask

What is Codable and Codable in Swift?

Codable is the combined protocol of Swift's Decodable and Encodable protocols. Together they provide standard methods of decoding data for custom types and encoding data to be saved or transferred.

What is Codable used for?

A type that can be used as a key for encoding and decoding. A type that can be converted to and from a coding key. A user-defined key for providing context during encoding and decoding.

What is Codable data?

What is Codable? Codable is to convert the JSON data object to an actual Swift class or struct and vice versa. It's usually used when the case you wanna retrieve or parse JSON data from the server like the APIs.

Is Dictionary Codable in Swift?

A lot of Swift's built-in types already conform to Codable by default. For example, Int , String , and Bool are Codable out of the box. Even dictionaries and arrays are Codable by default as long as the objects that you store in them conform to Codable .


1 Answers

Currently, Apple's Codable protocol does not have a way to decode XML.

While there are plenty of third party libraries to parse XML, the XMLParsing library contains a XMLDecoder and a XMLEncoder that uses Apple's own Codable protocol, and is based on Apple's JSONEncoder/JSONDecoder with changes to fit the XML standard.

Link: https://github.com/ShawnMoore/XMLParsing


W3School's XML To Parse:

<note>     <to>Tove</to>     <from>Jani</from>     <heading>Reminder</heading>     <body>Don't forget me this weekend!</body> </note> 

Swift Struct conforming to Codable:

struct Note: Codable {     var to: String     var from: String     var heading: String     var body: String } 

XMLDecoder:

let data = Data(forResource: "note", withExtension: "xml") else { return nil }  let decoder = XMLDecoder()  do {    let note = try decoder.decode(Note.self, from: data) } catch {    print(error) } 

XMLEncoder:

let encoder = XMLEncoder()  do {    let data = try encoder.encode(self, withRootKey: "note")     print(String(data: data, encoding: .utf8)) } catch {    print(error) } 

There are a number of benefits for using Apple's Codable protocol over that of a third-party's protocol. Take for example if Apple decides to begin supporting XML, you would not have to refactor.

For a full list of examples of this library, see the Sample XML folder in the repository.


There are a few differences between Apple's Decoders and Encoders to fit the XML standard. These are as follows:

Differences between XMLDecoder and JSONDecoder

  1. XMLDecoder.DateDecodingStrategy has an extra case titled keyFormatted. This case takes a closure that gives you a CodingKey, and it is up to you to provide the correct DateFormatter for the provided key. This is simply a convenience case on the DateDecodingStrategy of JSONDecoder.
  2. XMLDecoder.DataDecodingStrategy has an extra case titled keyFormatted. This case takes a closure that gives you a CodingKey, and it is up to you to provide the correct data or nil for the provided key. This is simply a convenience case on the DataDecodingStrategy of JSONDecoder.
  3. If the object conforming to the Codable protocol has an array, and the XML being parsed does not contain the array element, XMLDecoder will assign an empty array to the attribute. This is because the XML standard says if the XML does not contain the attribute, that could mean that there are zero of those elements.

Differences between XMLEncoder and JSONEncoder

  1. Contains an option called StringEncodingStrategy, this enum has two options, deferredToString and cdata. The deferredToString option is default and will encode strings as simple strings. If cdata is selected, all strings will be encoded as CData.

  2. The encode function takes in two additional parameters than JSONEncoder does. The first additional parameter in the function is a RootKey string that will have the entire XML wrapped in an element named that key. This parameter is required. The second parameter is an XMLHeader, which is an optional parameter that can take the version, encoding strategy and standalone status, if you want to include this information in the encoded xml.

like image 123
S.Moore Avatar answered Oct 01 '22 21:10

S.Moore