在 Swift 中轻松处理 XML。
将以下依赖项添加到您的 Package.swift
文件中
.package(url: "https://github.com/sersoft-gmbh/xmlwrangler", from: "6.0.0"),
Swift | XMLWrangler 包 |
---|---|
< 5.2.0 | 1.x.y - 3.x.y |
>= 5.3.0, < 5.9.0 | 5.x.y |
>= 5.9.0 | 6.x.y |
XML 中的每个元素都由 XMLElement
结构体表示。它具有三个属性,name
反映元素的标签名称,attributes
包含元素的所有属性,以及 content
描述元素的内容。内容是一个集合,其 Element
是一个枚举。该枚举有两种情况:.string
和 .element
。集合中的顺序是内容被找到的顺序。因此,如果一个元素首先包含一些文本,然后包含一个子元素,最后又包含一些文本,则 content
将包含一个 .string
,其关联的 StringPart
是第一个文本。接下来将是一个 .element
,其关联的 XMLElement
将是子元素。最后,将是另一个 .string
,其中包含最后一个文本。
虽然您可以创建一个 XMLElement
,其内容为 [.string("abc"), .string("def"), .element(XMLElement(name: "test"))]
,并且它也会生成有效的 XML,但可以将其清理为 [.string("abcdef"), .element(XMLElement(name: "test"))]
。为了实现这一点,建议使用 XMLElement.content
上的各种 append
函数,或者甚至直接在 XMLElement
上使用,当您不能保证内容在创建时被清理的情况下。如果您的元素是使用空内容 ([]
) 创建的,并且您追加上面的每个内容元素,append
函数会确保它们将 “def” 字符串追加到第一个 “abc” 字符串,而不是向内容添加另一个 .string
。如果由于某种原因,您仍然遇到内容具有连续 .string
元素的情况,则有一个方便的函数 compress()
(或其非变异兄弟函数 compressed()
),它将这些 .string
元素合并为一个。
一个 XMLElement
可以与另一个元素进行比较,并且如果所有三个属性(name
、attributes
和 content
)都相等,则被认为是相等的。这意味着对于一个大型树,根元素的所有子元素都将被比较。因此,在比较大型树时要小心,并在必要时回退到手动比较 name
和/或 attributes
。 XMLElement
也符合 Identifiable
协议,并使用 name
作为 id
。
使用 XMLWrangler 序列化和解析 XML 都依赖于 XMLElement
。
可以使用 XMLElement
上的静态函数来解析现有的 XML。您可以解析给定的 Data
对象或包含 XML 的 String
。如果解析成功,则返回解析后的根对象。否则,沿途发生的任何错误都会被抛出。抛出的错误是由 Foundation.XMLParser
创建的错误。
do {
let xml = """
<?xml version='1.0' encoding='UTF-8'?>
<root myattr='myvalue'>
<child1/>
<child2>some text</child2>
</root>
"""
let root = try XMLElement.parse(xml)
} catch {
print("Something went wrong while parsing: \(error)")
}
在此示例中,root.name.rawValue
当然将是 "root"
。 root.content
将包含两个 .element
。第一个将具有关联的 XMLElement
,其 name
为 "child1"
,内容为空 content
。第二个 .element
的 XMLElement
的 name
将是 "child2"
,其内容将包含一个 .string
,其中关联了 "some text"
。 root.attributes
将包含键 "myattr"
的值 "myvalue"
。
由于您可以解析 XML,因此您也可以将 XMLElement
转换为 String。为此,XMLElement
上有两个函数。第一个函数只是将 XMLElement
转换为 String
。这通过创建开始和结束标签(其中开始标签包含 attributes
,如果可用)并将元素的 content
放在两者之间来实现。此外,content
在序列化之前会被压缩(使用前面提到的 compress
函数)。
var root = XMLElement(name: "root", attributes: ["myattr": "myvalue"])
root.content.append(element: "child1")
root.content.append(element: XMLElement(name: "child2", content: "some text"))
let xmlString = xml.serialize() // -> "<root myattr=\"myvalue\"><child1/><child2>some text</child2></root>"
如果还应添加传统的 XML 标头,则还有第二个函数,它接受版本和文档编码作为附加参数,但在其他方面遵循相同的规则
var root = XMLElement(name: "root", attributes: ["myattr": "myvalue"])
root.content.append(element: "child1")
root.content.append(element: XMLElement(name: "child2", content: "some text"))
let xmlDocumentString = root.serializeAsDocument(at: DocumentVersion(major: 1), using: .utf8)
// -> "<?xml version=\"1.0\" encoding=\"UTF-8\"?><root myattr=\"myvalue\"><child1/><child2>some text</child2></root>"
请注意,XMLWrangler 不会根据给定的编码转义字符串。它只是使用它来生成文档标头。
这两个函数都可以接受一个附加参数 options
,其中包含一组控制序列化行为的选项。目前,以下选项是可能的
.pretty
: 使用美观的格式。这会在标签周围添加换行符,使生成的 XML 更易于阅读。这通常不是处理 XML 所必需的。.singleQuoteAttributes
: 当此选项存在时,元素的属性将用单引号 (') 而不是双引号 (") 括起来。.explicitClosingTag
: 此选项强制空元素使用显式结束标签进行序列化,而不是使用简写 />
语法。XMLWrangler 将始终在内部将所有内容和属性提取为 String
。这是因为 XML 本身不区分类型,例如 JSON 那样。但是,有许多辅助函数可以安全地查找和转换 XMLElement
的内容和属性
XMLElement.elements(named:)
XMLElement.element(at:)
XMLElement.attribute(for:)
。RawRepresentable
,您无需传递 converter
):XMLElement.convertedAttribute(for:converter:)
XMLElement.stringContent()
XMLElement.convertedStringContent(converter:)
当出现问题时,所有这些方法都会抛出一个错误 (XMLElement.LookupError
),而不是返回可选值。如果您更喜欢可选值,您可以始终使用 try?
。有关更多信息,请查看标题文档,其中更详细地描述了这些方法。
虽然尚未集成,但以下功能可能会提供附加值,并可能在未来加入 XMLWrangler
API 使用标题文档进行文档化。如果您更喜欢将文档作为网页查看,则有一个 在线版本 可供您使用。
如果您发现 XMLWrangler 中存在错误/希望看到新功能,可以通过以下几种方式提供帮助
请参阅 LICENSE 文件。
版权所有 © 2016-2023 ser.soft GmbH。