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

Implementing a custom Decoder in Swift 4

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

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

我的问题是我不知道如何实施 Decoder 协议,而且我似乎无法在网上找到任何资源来解释它是如何完成的。我发现的每个资源都严格提到 Swift 标准库中包含的 JSONDecoder class,我发现没有任何资源解决创建您自己的自定义解码器的问题。

我还没有机会将我的代码变成一个框架,但你可以看看我的 Github 存储库,它为 XML 实现了自定义解码器和编码器。

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

编码器和解码器位于存储库的 XML 文件夹中。它基于 Apple 的 JSONEncoder 和 JSONDecoder,并进行了更改以适应 XML 标准。


XML解码器和 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的选项,这个枚举有两个选项,deferredToStringcdatadeferredToString 选项是默认选项,会将字符串编码为简单字符串。如果选择cdata,则所有字符串将被编码为CData。

  2. encode 函数比 JSONEncoder 多接收两个参数。该函数中的第一个附加参数是一个 RootKey 字符串,它将整个 XML 包裹在一个名为该键的元素中。此参数是必需的。第二个参数是一个XMLHeader,这是一个可选参数,可以带版本,编码策略和独立状态,如果你想在编码的xml.

    中包含这些信息

例子

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

XML 解析:

<?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 结构:

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"
}

XML解码器:

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)
}

XML编码器:

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)
}