在Swift 4中实现自定义解码器

Ton*_*čić 30 xml decoder swift swxmlhash codable

我想使用DecodableSwift 4中引入的新协议解码XML文档,但是,似乎没有符合Decoder协议的XML解码器的现有实现.

我的计划是使用SWXMLHash库来解析XML,然后可能使该XMLIndexer库中的类扩展Decoder协议,以便可以使用XMLIndexer(XMLIndexer返回SWXMLHash.parse(xmlString))实例初始化我的模型.

XMLIndexer + Decoder.swift

我的问题是我不知道如何实现Decoder协议,我似乎无法在网上找到任何解释它是如何完成的资源.我发现的每一个资源都严格地提到了JSONDecoderSwift标准库中包含的类,没有找到的资源解决了创建自己的自定义解码器的问题.

S.M*_*ore 34

我还没有机会将我的代码转换为框架,但你可以看看我的Github Repository,它实现了自定义解码器和XML编码器.

链接:https://github.com/ShawnMoore/XMLParsing

编码器和解码器驻留在repo的XML文件夹中.它基于Apple的JSONEncoder和JSONDecoder,其更改符合XML标准.


XMLDecoder和JSONDecoder之间的差异

  1. XMLDecoder.DateDecodingStrategy有一个额外的案例标题keyFormatted.这种情况采用一个闭包,为您提供CodingKey,由您提供所提供密钥的正确DateFormatter.这只是JSONDecoder的DateDecodingStrategy 上的一个简便案例.
  2. XMLDecoder.DataDecodingStrategy有一个额外的案例标题keyFormatted.这种情况采用一个闭包,为您提供CodingKey,由您提供正确的数据或nil为提供的密钥.这只是JSONDecoder的DataDecodingStrategy 上的一个简便案例.
  3. 如果符合Codable协议的对象具有数组,并且正在解析的XML不包含数组元素,则XMLDecoder将为该属性分配一个空数组.这是因为XML标准表示如果XML不包含该属性,则可能意味着这些元素中没有任何元素.

XMLEncoder和JSONEncoder之间的差异

  1. 包含一个名为的选项StringEncodingStrategy,这个枚举有两个选项,deferredToStringcdata.该deferredToString选项是默认的,将编码字符串作为简单的字符串.如果选择了cdata,则所有字符串都将编码为CData.

  2. encode函数接受了比JSONEncoder更多的两个参数.函数中的第一个附加参数是RootKey字符串,它将整个XML包装在名为该键的元素中.此参数是必需的.第二个参数是XMLHeader,它是一个可选参数,如果要在编码的xml中包含此信息,则可以采用版本,编码策略和独立状态.


例子

有关示例的完整列表,请参阅存储库中的Sample XML文件夹.

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>
Run Code Online (Sandbox Code Playgroud)

Swift结构:

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"
}
Run Code Online (Sandbox Code Playgroud)

的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)
}
Run Code Online (Sandbox Code Playgroud)

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)
}
Run Code Online (Sandbox Code Playgroud)