这个包允许通过 Swift 4 中引入的 Codable 协议来编码/解码任意 XML 文档。
免责声明: 开发这个包是为了在我自己的项目中使用。我将其作为开源发布,希望它对其他开发者有用。虽然我设计的这个实现具有合理的通用性,但我并不声称它能满足所有需求。但是,如果您发现 bug 或缺少某个功能,欢迎提交 issue 或 pull request,最好是提供一个失败的测试(或者更好,一个 bug 修复或可用的功能)。我接受所有建议。
支持的平台: macOS 10.12+, Ubuntu 18.04, Ubuntu 20.04.
支持的 Swift 版本 5.1 ... 5.6.
CodableXML 依赖于 Swift Package Manager 来集成到您的项目中。
Package.swift
文件中的 dependencies
列表.package(url: "https://github.com/franklefebvre/XMLCoder.git", equal: "0.3.3"),
XMLCoder
依赖项添加到您的 target.target(
name: <your_target_here>,
dependencies: ["XMLCoder", ...]),
import XMLCoder
您可以通过选择 File > Swift Packages > Add Package Dependency... 并提供仓库的 URL:https://github.com/franklefebvre/XMLCoder,将 XMLCoder 添加到现有项目中。当前版本仍在开发中,API 的某些部分可能会更改,因此我建议在 Version 菜单中选择“Up to Next Minor”或“Exact”。
该库编码到/解码自 Foundation 提供的 XMLDocument
对象。
我选择不处理 XML 的文本(或数据)的原因有很多:
缺点是 Linux 上使用的 Foundation 的开源实现与 macOS 版本不同。编码器生成的 XML 结构可能略有不同。
假设 Document
符合 Encodable
,编码可以像这样简单:
func encode(doc: Document) throws -> String {
let encoder = XMLEncoder(documentRootTag: "root")
let xmlDocument = try encoder.encode(doc)
// here xmlDocument is of type XMLDocument
guard let result = String(data: xmlDocument.xmlData, encoding: .utf8) else {
throw SomeError()
}
return result
}
假设 Document
符合 Decodable
,可以使用以下方法解码 XML 字符串:
func decode(xmlString: String) throws -> Document {
let xmlDocument = XMLDocument(...)
let decoder = XMLDecoder()
let document = try decoder.decode(Document.self, from: xmlDocument)
return document
}
XMLCoder 支持大多数 XML 特定的特性,例如命名空间、属性等。这些特性通过对 CodingKeys
协议的扩展来实现。
可以通过利用编译器为您的 Codable 类型合成的代码来编码/解码简单的 XML 文档。但是,当 XML 表示形式更复杂时,必须在 codable 类型旁边提供显式的 CodingKeys 声明。
为了支持命名空间,CodingKeys
类型必须符合 XMLQualifiedKey
协议。
该协议声明如下:
protocol XMLQualifiedKey {
var namespace: String? { get }
}
因此,对于 CodingKeys
类型中定义的每个键,如果该键属于默认命名空间,则 namespace
可以返回命名空间 URI,或者返回 nil
。
struct NamespaceStruct: Codable {
var key1: String
var key2: String
private enum CodingKeys: String, CodingKey, XMLQualifiedKey {
case key1
case key2
var namespace: String? {
switch self {
case .key1:
return "http://namespace.example.com"
default:
return nil
}
}
}
}
let value = NamespaceStruct(key1: "element1", key2: "element2")
let encoder = XMLEncoder(documentRootTag: "root")
let xmlDocument = try encoder.encode(value)
let result = String(data: xmlDocument.xmlData, encoding: .utf8)
结果字符串将包含类似以下内容:
<root xmlns:ns1="http://namespace.example.com">
<ns1:key1>element1</ns1:key1>
<key2>element2</key2>
</root>
通过将 XMLTypedKey
协议添加到 CodingKeys
来实现属性和数组。
protocol XMLTypedKey {
var nodeType: XMLNodeType { get }
}
enum XMLNodeType {
case element
case attribute
case inline
case array(String?)
}
element
时,它的值被表示为一个 XML 元素。这是默认行为。attribute
时,它成为父节点的属性(参见下面的“属性示例”)。inline
时,它的值被表示为一个没有封闭元素的字符串(即,它属于父元素)。array
时,它的值被表示为一个 XML 元素序列,这些 XML 元素的键是枚举 case 的关联值,默认为 element
(参见下面的“数组示例”)。使用此定义:
struct AttributeStruct: Codable {
var key1: String
var key2: String
private enum CodingKeys: String, CodingKey, XMLTypedKey {
case key1
case key2
var nodeType: XMLNodeType {
switch self {
case .key1:
return .attribute
case .key2:
return .element
}
}
}
}
let value = AttributeStruct(key1: "value1", key2: "value2")
let encoder = XMLEncoder(documentRootTag: "root")
let xmlDocument = try encoder.encode(value)
let result = String(data: xmlDocument.xmlData, encoding: .utf8)
结果字符串将包含类似以下内容:
<root key1="value1">
<key2>value2</key2>
</root>
使用此定义:
struct ArrayStruct: Codable {
var key3: String
var key4: [String]
private enum CodingKeys: String, CodingKey, XMLNodeType {
case key3
case key4
var nodeType: XMLNodeType {
switch(self) {
case .key3:
return .element
case .key4:
return .array("child")
}
}
}
}
let value = ArrayStruct(key3: "value3", key4: ["one", "two", "three"])
let encoder = XMLEncoder(documentRootTag: "root")
let xmlDocument = try encoder.encode(value)
let result = String(data: xmlDocument.xmlData, encoding: .utf8)
结果字符串将包含类似以下内容:
<root>
<key3>value3</key3>
<key4>
<child>one</child>
<child>two</child>
<child>three</child>
</key4>
</root>
这可能会在未来的实现中发生变化。
必须使用表示文档根的标签名称初始化 XMLEncoder
。
默认情况下,XMLDecoder
会忽略 XML 文档中的根标签。但是,可以使用 documentRootTag
属性指定根标签的预期名称。
默认情况下,Swift 类型中的键被编码和解码为具有相同名称的 XML 标签,无需转换。但是,可以使用 XMLEncoder
和 XMLDecoder
上的 keyCodingStrategy
、elementNameCodingStrategy
和 attributeNameCodingStrategy
属性全局更改此行为。
keyCodingStrategy
定义元素和属性的行为,除非被 elementNameCodingStrategy
或 attributeNameCodingStrategy
覆盖。
可能的设置是:
useDefaultKeys
:这是默认行为,如上所述。custom
:允许提供一个闭包来实现转换。该闭包始终定义从编码键到 XML 元素和/或属性名称的转换。这允许在编码和解码中使用相同的函数。除了这种全局机制之外,仍然可以在个案基础上进行键和标签之间的转换,例如,通过为 CodingKeys
枚举中的枚举 case 显式提供字符串。
某些类型在 XML 中没有原生表示形式,可能需要额外的转换才能在 XML 元素或属性中表示为字符串。
XMLEncoder
的 nilEncodingStrategy
和 XMLDecoder
的 nilDecodingStrategy
确定如何在 XML 文档中表示可选值。
可能的设置是:
missing
:XML 文档中不存在与 nil 值相对应的 XML 元素或属性。这是默认行为。empty
:nil 值在 XML 文档中表示为空元素或值为空字符串的属性。XMLEncoder
公开了一个 boolEncodingStrategy
属性,XMLDecoder
公开了一个对称的 boolDecodingStrategy
属性。这些属性被定义为包含用于表示 false
和 true
值的字符串的双元素结构。
默认情况下,false
表示为 "0"
,true
表示为 "1"
。
XMLEncoder
公开了一个 dataEncodingStrategy
属性,XMLDecoder
公开了一个对称的 dataDecodingStrategy
属性。这些属性允许将 Data 属性表示为 Base64 字符串(默认),十六进制字符串,或提供自定义实现。
XMLEncoder
公开了一个 dateEncodingStrategy
属性,XMLDecoder
公开了一个对称的 dateDecodingStrategy
属性。默认行为是将日期编码和解码为 ISO 8601 字符串;其他策略允许提供 DateFormatter
或自定义实现。
URL 被转换为/从字符串转换。不提供自定义选项。