Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing a custom Decoder in Swift 4

I'd like to decode an XML document using the new Decodable protocol introduced in Swift 4, however, there doesn't seem to be an existing implementation for an XML decoder that conforms to the Decoder protocol.

My plan was to use the SWXMLHash library to parse the XML, then possibly make the XMLIndexer class in that library extend the Decoder protocol so that my model can be initialized with an instance of XMLIndexer (XMLIndexer is returned by SWXMLHash.parse(xmlString)).

XMLIndexer+Decoder.swift

My issue is that I have no clue how to implement the Decoder protocol and I can't seem to find any resources online that explain how it's done. Every resource that I've found strictly mentions the JSONDecoder class which is included with the Swift standard library and no resource I've found addresses the issue of creating your own custom decoder.

like image 249
Toni Sučić Avatar asked Aug 20 '17 23:08

Toni Sučić


People also ask

What is the difference between Codable and Decodable in Swift?

Understanding what Swift's Codable isWhen you only want to convert JSON data into a struct, you can conform your object to Decodable . If you only want to transform instances of your struct into Data , you can conform your object to Encodable , and if you want to do both you can conform to Codable .

What is Codingkey Swift?

A type that can be used as a key for encoding and decoding.

What is Codable in IOS 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.


1 Answers

I haven't had a chance to turn my code into a framework yet, but you can take a look at my Github Repository that implements both a custom decoder and encoder for XML.

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

The encoder and decoder resides in the XML folder of the repo. It is based on Apple's JSONEncoder and JSONDecoder with changes to fit the XML standard.


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.


Examples

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

XML To Parse:

<?xml version="1.0"?> <book id="bk101">     <author>Gambardella, Matthew</author>     <title>XML Developer's Guide</title>     <genre>Computer</genre>     <price>44.95</price>     <publish_date>2000-10-01</publish_date>     <description>An in-depth look at creating applications         with XML.</description> </book> 

Swift Structs:

struct Book: Codable {     var id: String     var author: String     var title: String     var genre: Genre     var price: Double     var publishDate: Date     var description: String          enum CodingKeys: String, CodingKey {         case id, author, title, genre, price, description                  case publishDate = "publish_date"     } }  enum Genre: String, Codable {     case computer = "Computer"     case fantasy = "Fantasy"     case romance = "Romance"     case horror = "Horror"     case sciFi = "Science Fiction" } 

XMLDecoder:

let data = Data(forResource: "book", withExtension: "xml") else { return nil }          let decoder = XMLDecoder()          let formatter: DateFormatter = {    let formatter = DateFormatter()    formatter.dateFormat = "yyyy-MM-dd"    return formatter }()          decoder.dateDecodingStrategy = .formatted(formatter)          do {    let book = try decoder.decode(Book.self, from: data) } catch {    print(error) } 

XMLEncoder:

let encoder = XMLEncoder()          let formatter: DateFormatter = {    let formatter = DateFormatter()    formatter.dateFormat = "yyyy-MM-dd"    return formatter }()          encoder.dateEncodingStrategy = .formatted(formatter)          do {    let data = try encoder.encode(self, withRootKey: "book", header: XMLHeader(version: 1.0))                 print(String(data: data, encoding: .utf8)) } catch {    print(error) } 
like image 170
S.Moore Avatar answered Sep 25 '22 14:09

S.Moore