使用 Swift 实现的简单 XML 解析器
这是什么?
这是一个受 SwiftyJSON 和 SWXMLHash 启发的 XML 解析器。
Foundation 框架中的 NSXMLParser 是一种“SAX”解析器。它性能足够好,但使用上略有不便。因此,我们实现了一个将“DOM”解析器包装在它周围。
特性
- 通过“索引”访问 XML 文档。
- 将 XML 文档作为序列访问。
- 易于调试 XML 路径。
要求
- iOS 9.0+
- tvOS 9.0+
- macOS 10.10+
- Swift 5.0
安装
Carthage
1. 创建 Cartfile
github "https://github.com/yahoojapan/SwiftyXMLParser"
2. 安装
> carthage update
CocoaPods
1. 创建 Podfile
platform :ios, '9.0'
use_frameworks!
pod "SwiftyXMLParser", :git => 'https://github.com/yahoojapan/SwiftyXMLParser.git'
2. 安装
> pod install
示例
import SwiftyXMLParser
let str = """
<ResultSet>
<Result>
<Hit index=\"1\">
<Name>Item1</Name>
</Hit>
<Hit index=\"2\">
<Name>Item2</Name>
</Hit>
</Result>
</ResultSet>
"""
// parse xml document
let xml = try! XML.parse(str)
// access xml element
let accessor = xml["ResultSet"]
// access XML Text
if let text = xml["ResultSet", "Result", "Hit", 0, "Name"].text {
print(text)
}
if let text = xml.ResultSet.Result.Hit[0].Name.text {
print(text)
}
// access XML Attribute
if let index = xml["ResultSet", "Result", "Hit", 0].attributes["index"] {
print(index)
}
// enumerate child Elements in the parent Element
for hit in xml["ResultSet", "Result", "Hit"] {
print(hit)
}
// check if the XML path is wrong
if case .failure(let error) = xml["ResultSet", "Result", "TypoKey"] {
print(error)
}
使用方法
1. 解析XML
- 从字符串
let str = """
<ResultSet>
<Result>
<Hit index=\"1\">
<Name>Item1</Name>
</Hit>
<Hit index=\"2\">
<Name>Item2</Name>
</Hit>
</Result>
</ResultSet>
"""
xml = try! XML.parse(str) // -> XML.Accessor
- 从NSData
let str = """
<ResultSet>
<Result>
<Hit index=\"1\">
<Name>Item1</Name>
</Hit>
<Hit index=\"2\">
<Name>Item2</Name>
</Hit>
</Result>
</ResultSet>
"""
let string = String(decoding: data, as: UTF8.self)
xml = XML.parse(data) // -> XML.Accessor
- 包含无效字符
let srt = "<xmlopening>@ß123\u{1c}</xmlopening>"
let xml = XML.parse(str.data(using: .utf8))
if case .failure(XMLError.interruptedParseError) = xml {
print("invalid character")
}
更多内容,请参阅 https://developer.apple.com/documentation/foundation/xmlparser/errorcode
2. 访问子元素
let element = xml.ResultSet // -> XML.Accessor
3. 访问孙元素
- 使用字符串
let element = xml["ResultSet"]["Result"] // -> <Result><Hit index=\"1\"><Name>Item1</Name></Hit><Hit index=\"2\"><Name>Item2</Name></Hit></Result>
- 使用数组
let path = ["ResultSet", "Result"]
let element = xml[path] // -> <Result><Hit index=\"1\"><Name>Item1</Name></Hit><Hit index=\"2\"><Name>Item2</Name></Hit></Result>
- 使用可变参数
let element = xml["ResultSet", "Result"] // -> <Result><Hit index=\"1\"><Name>Item1</Name></Hit><Hit index=\"2\"><Name>Item2</Name></Hit></Result>
- 使用@dynamicMemberLookup
let element = xml.ResultSet.Result // -> <Result><Hit index=\"1\"><Name>Item1</Name></Hit><Hit index=\"2\"><Name>Item2</Name></Hit></Result>
4. 访问特定的孙元素
let element = xml.ResultSet.Result.Hit[1] // -> <Hit index=\"2\"><Name>Item2</Name></Hit>
5. 访问元素中的属性
if let attributeValue = xml.ResultSet.Result.Hit[1].attributes?["index"] {
print(attributeValue) // -> 2
}
6. 在元素中访问文本
- 带有可选绑定
if let text = xml.ResultSet.Result.Hit[1].Name.text {
print(text) // -> Item2
}
- 带有自定义操作
struct Entity {
var name = ""
}
let entity = Entity()
entity.name ?= xml.ResultSet.Result.Hit[1].Name.text // assign if it has text
- 转换整型并赋值
struct Entity {
var name: Int = 0
}
let entity = Entity()
entity.name ?= xml.ResultSet.Result.Hit[1].Name.int // assign if it has Int
还有其他语法糖,包括布尔型、URL和双精度型。
- 将文本赋值给数组
struct Entity {
var names = [String]()
}
let entity = Entity()
entity.names ?<< xml.ResultSet.Result.Hit[1].Name.text // assign if it has text
7. 访问CDATA
let str = """
<Data name="DATE">
<value><![CDATA[2018-07-08]]></value>
</Data>
"""
// parse xml document
let xml = try! XML.parse(str)
if let cdata = xml.Data.value.element?.CDATA,
let cdataStr = String(data: cdata, encoding: .utf8) {
print(cdataStr) // -> "2018-07-08"
}
7. 计算子元素数量
let numberOfHits = xml.ResultSet.Result.Hit.all?.count
8. 检查错误
print(xml.ResultSet.Result.TypoKey) // -> "TypoKey not found."
9. 作为序列类型访问
- for-in
for element in xml.ResultSet.Result.Hit {
print(element.text)
}
- map
xml.ResultSet.Result.Hit.map { $0.Name.text }
10. 生成XML文档
print(Converter(xml.ResultSet).makeDocument())
使用Alamofire
SwiftyXMLParser 与 Alamofire 配合良好。您可以轻松解析响应。
import Alamofire
import SwiftyXMLParser
Alamofire.request(.GET, "https://itunes.apple.com/us/rss/topgrossingapplications/limit=10/xml")
.responseData { response in
if let data = response.data {
let xml = XML.parse(data)
print(xml.feed.entry[0].title.text) // outputs the top title of iTunes app raning.
}
}
此外,还有 Alamofire 的扩展,可以与 SwiftyXMLParser 结合使用。
迁移指南
当前主分支 支持 Xcode10。如果您想使用与此库兼容的旧 swift 版本,请阅读 发布说明 并安装最后一个兼容版本。
许可协议
本软件遵循 MIT 许可协议发布,请参阅 LICENSE 文件。