iOS 使用 XMLParser
类来解析XML数据,一般的流程是根据url
创建XMLParser
对象,设置XMLParser
对象的代理,然后调用parse
方法开始解析,解析过程中主要用到的代理方法:
func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
//每次经过开始节点的时候调用
}
func parser(_ parser: XMLParser, foundCharacters string: String) {
//获取到有用的字段的时候调用
}
func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
//经过结束节点的时候调用
}
一般的解析过程都是在上面三个代理方法中进行,需要考虑到每一个节点的elementName
并及时做判断
这里介绍一种比较优雅的解析方法:
设计一个类,每次经过某个节点的时候都是一个具体的对象作为 XMLParser 的代理来处理该节点所包含的数据,到达下一级节点的时候,当前的代理对象创建一个子对象然后将代理交给子对象来处理对应的节点数据,处理完之后,子对象将代理交还给父对象并交出得到的数据,最后将自己销毁。
下面是初始的设计代码
class XMLParserDelegateModel: NSObject {
var name:String! // 保存当前节点的名称并且作为自身的名称
var text = "" //用来保存解析到的值
weak var parent:XMLParserDelegateModel? // 该节点对应的上一个层级的节点(考虑到最顶端的节点所以可以为nil)
var child:XMLParserDelegateModel! // 如果不是最底层的数据,应该有子节点
// 方便父节点来创建子节点解析对象
required init(_ name:String,_ parent:XMLParserDelegateModel) {
}
// 该方法方便父节点创建子节点并且将代理传递给子节点解析对象
func makeChild(klass:XMLParserDelegateModel.Type,
elementName:String,
parser:XMLParser) {
}
// 子节点解析对象在交还代理之前在这里将信息交出
func finishedChild(text:String) {
}
}
根据描述来补全代码,显然该类还应该遵守XMLParserDelegate
协议
class XMLParserDelegateModel: NSObject {
var name:String!
var text:String = ""
weak var parent:XMLParserDelegateModel?
var child:XMLParserDelegateModel!
required init(_ name:String,_ parent:XMLParserDelegateModel) {
self.name = name
self.parent = parent
super.init()
}
func makeChild(klass:XMLParserDelegateModel.Type,
elementName:String,
parser:XMLParser) {
let child = klass.init(elementName, self)
self.child = child
parser.delegate = self.child
}
func finishedChild(_ text:String) {
fatalError("Subclass must implement finishedChild(_ :)")
}
}
extension XMLParserDelegateModel:XMLParserDelegate{
func parser(_ parser: XMLParser, foundCharacters string: String) {
text += string
}
func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
if parent != nil {
parent!.finishedChild(text)
parser.delegate = parent
}
}
}
这样就设计出原始的解析类,下面来看一个具体的XML例子:
<?xml version="1.0" encoding="utf-8"?>
<root>
<branch>
<name>西環分行</name>
<address>香港西營盤皇后大道西242-244號</address>
<parent>香港島區</parent>
<location>22.286909,114.143288</location>
<phone>(852)218 95588</phone>
<fax>(852)2559 8442</fax>
<tag>1/2/7/5/4</tag>
</branch>
<branch>
<name>上環分行</name>
<address>香港上環德輔道中317-319號啟德商業大廈地下F舖</address>
<parent>香港島區</parent>
<location>22.286949,114.150673</location>
<phone>(852)218 95588</phone>
<fax>(852)2545 7458</fax>
<tag>1/2/7/3/5/4</tag>
</branch>
根据具体的XML数据结构来设计不同的节点类作为不同节点的代理来进行解析
class Branch: NSObject {
// 模型类,将属性补全即可
}
class BranchXMLParserDelegate: XMLParserDelegateModel {
var b = Branch()
func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
self.makeChild(klass: XMLParserDelegateModel.self, elementName: elementName, parser: parser)
}
override func finishedChild(_ s: String) {
b.setValue(s, forKey: child.name)
}
}
class RootXMLParserDelegate: XMLParserDelegateModel {
var bs = [Branch]()
func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
if elementName == "branch" {
self.makeChild(klass: BranchXMLParserDelegate.self, elementName: elementName, parser: parser)
}
}
override func finishedChild(_ s: String) {
bs.append((child as! BranchXMLParserDelegate).b)
}
}
override func viewDidLoad() {
super.viewDidLoad()
if let url = Bundle.main.url(forResource: "test", withExtension: "xml"){
if let parser = XMLParser(contentsOf: url) {
let root = RootXMLParserDelegate(name: "", parent: nil)
parser.delegate = root
parser.parse()
}
}else{
print("No such xml file")
}
}
原思想来自 <<Programming iOS 9>>