返回 导航

Swift

hangge.com

Swift - 解析XML格式数据(分别使用GDataXML和KissXML)

作者:hangge | 2015-03-06 13:58
在做一些应用的时候经常需要用到XML解析,比如获取Web Service数据,读取RSS新闻或者博客数据源。下面演示了两个非常方便高效的XML库在Swift里的调用方法。
假设需要被解析的XML数据文件users.xml如下:
<?xml version="1.0" encoding="utf-8"?>  
<Users>  
    <User id="101">
        <name>航歌</name>
        <tel>
        	<mobile>1234567</mobile>
        	<home>025-8100000</home>
        </tel>  
    </User>  
    <User id="102">
        <name>hangge</name>
        <tel>
        	<mobile>8989889</mobile>
        	<home>025-8122222</home>
        </tel> 
    </User>  
</Users>
我们需要实现的功能是解析并打印出需要的数据:
User: uid:101,uname:航歌,mobile:1234567,home:025-8100000
User: uid:102,uname:hangge,mobile:8989889,home:025-8122222

一,使用GDataXML(这个是google出品的) 
1,GitHub主页地址:https://github.com/google/gdata-objectivec-client

2,在build phases -> Link Binary With Libraries中,点击“+”添加“libxml2.2.tbd”
 


3,在build setting -> Header Search Paths里添加 ${SDK_DIR}/usr/include/libxml2
 

4,在build setting里的Objective-C编译选项里,把自动引用计数改为No,否则在应用GDataXML库时会编译有错
 

5,添加bridge.h头文件并设置到编译参数里
#import "GDataXMLNode.h"
6,导入GDataXML库文件(GDataXMLNode.h和GDataXMLNode.m),代码结构如下:
 

7,开始解析(ViewController.swift)
import UIKit

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let label:UILabel = UILabel(frame:CGRect(x: 100, y: 100, width: 300, height: 100))
        label.text = "输出结果在控制台"
        self.view.addSubview(label)
        //测试Swift调用Object的XML库功能
        testXML()
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    func testXML() {
        //获取xml文件路径

        let file = Bundle.main.path(forResource: "users", ofType: "xml")
        let url = URL(fileURLWithPath: file!)
        //获取xml文件内容
        let xmlData = try! Data(contentsOf: url)
        //可以转换为字符串输出查看
        //print(String(data:xmlData, encoding:String.Encoding.utf8))
        
        //使用NSData对象初始化文档对象
        //这里的语法已经把OC的初始化函数直接转换过来了
        let doc:GDataXMLDocument = try! GDataXMLDocument(data:xmlData, options : 0)
        
        //获取Users节点下的所有User节点,显式转换为element类型编译器就不会警告了
        //let users = doc.rootElement().elements(forName: "User") as! [GDataXMLElement]
        
        //通过XPath方式获取Users节点下的所有User节点,在路径复杂时特别方便
        let users = try! doc.nodes(forXPath: "//User", namespaces:nil) as! [GDataXMLElement]
        
        for user in users {
            //User节点的id属性
            let uid = user.attribute(forName: "id").stringValue()
            //获取name节点元素
            let nameElement = user.elements(forName: "name")[0] as! GDataXMLElement
            //获取元素的值
            let uname =  nameElement.stringValue()
            //获取tel子节点
            let telElement = user.elements(forName: "tel")[0] as! GDataXMLElement
            //获取tel节点下mobile和home节点
            let mobile = (telElement.elements(forName: "mobile")[0]
                as! GDataXMLElement).stringValue()
            let home = (telElement.elements(forName: "home")[0]
                as! GDataXMLElement).stringValue()
            //输出调试信息
            print("User: uid:\(uid!),uname:\(uname!),mobile:\(mobile!),home:\(home!)")
        }
    }
}
示例下载:
GDataXML.zip(2015-12-02 Swift2)
hangge_646_1.zip(2016-09-23 Swift3最新版)


二,使用KissXML(DDXML) 
GitHub主页地址:https://github.com/robbiehanson/KissXML

1,使用方法
(1)前面配置同GDataXML 的1,2,3步一样
(2)添加bridge.h头文件并设置到编译参数里
#import "DDXML.h"
#import "DDXMLElementAdditions.h"
(3)导入DDXML库文件

(4)开始解析(ViewController.swift)
import UIKit

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let label:UILabel = UILabel(frame:CGRect(x: 100, y: 100, width: 300, height: 100));
        label.text = "输出结果在控制台"
        self.view.addSubview(label)
        //测试Swift调用Object的XML库功能
        testXML()
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    func testXML() {
        //获取xml文件路径
        let file = Bundle.main.path(forResource: "users", ofType: "xml")
        let url = URL(fileURLWithPath: file!)
        //获取xml文件内容
        let xmlData = try! Data(contentsOf: url)
        
        //构造XML文档
        let doc = try! DDXMLDocument(data: xmlData, options:0)
        
        //利用XPath来定位节点(XPath是XML语言中的定位语法,类似于数据库中的SQL功能)
        let users = try! doc.nodes(forXPath: "//User") as! [DDXMLElement]
        for user in users {
            let uid = user.attribute(forName: "id")!.stringValue
            //DDXMLElementAdditions提供了elementForName获取单个节点,不用获取数组了
            let uname = user.forName("name")!.stringValue
            //获取tel节点的子节点
            let telElement = user.forName("tel")! as DDXMLElement
            let mobile = (telElement.forName("mobile")! as DDXMLElement).stringValue
            let home = (telElement.forName("home")! as DDXMLElement).stringValue
            print("User: uid:\(uid!),uname:\(uname!),mobile:\(mobile!),home:\(home!)")
        }
    }
}
示例下载:
DDXML.zip(2015-12-02 Swift2)
hangge_646_2.zip(2016-09-23 Swift3最新版)

2,DDXML获取子元素:forName(name: String)与elements(forName: String)
forName(name: String) 是根据name取同名的子结点,如果有多个 ,就只取第一个。
elements(forName: String) 是把所有的同名子节点都返回。
比如把上面XML数据文件users.xml每个人都添加多个mobile节点:
<?xml version="1.0" encoding="utf-8"?>  
<Users>  
    <User id="101">
        <name>航歌</name>
        <tel>
        	<mobile>0000001</mobile>
        	<mobile>1234567</mobile>
        	<mobile>9999999</mobile>
        	<home>025-8100000</home>
        </tel>  
    </User>  
    <User id="102">
        <name>hangge</name>
        <tel>
        	<mobile>8989889</mobile>
        	<mobile>1345678</mobile>
        	<home>025-8122222</home>
        </tel> 
    </User>  
</Users>
如果想把所有的mobile节点都遍历出来,就要使用elements(forName: String)
//let mobile = (telElement.forName("mobile")! as DDXMLElement).stringValue
for element in telElement.elements(forName:"mobile"){
    let mobile = element.stringValue
    print("mobile:\(mobile!)")
}

3,扩展DDXML方便使用
使用 DDXML elementForName 方法访问子节点时,要不断地拆箱装箱,如果节点层级深的话写起来就会比较麻烦。
这里对 DDXMLElement 类做了扩展,使其通过索引即可访问子节点。同时添加了个 attributeValue() 方法,便于获取属性值。
extension DDXMLElement {
    //通过索引获取子节点
    subscript(key: String) -> DDXMLElement {
        get {
            let r = self.forName(key)
            return r!
        }
    }
}
可以比较下原来的取值方式和现在的方式:
/********* 原始的 ****************/
let uid = user.attribute(forName: "id")!.stringValue
let uname = user.forName("name")!.stringValue

let telElement = user.forName("tel")! as DDXMLElement
let mobile = (telElement.forName("mobile")! as DDXMLElement).stringValue
let home = (telElement.forName("home")! as DDXMLElement).stringValue

print("User: uid:\(uid!),uname:\(uname!),mobile:\(mobile!),home:\(home!)")


/******** 扩展后的 *****************/
let uid = user.attribute(forName: "id")!.stringValue
let uname = user["name"].stringValue

let mobile = user["tel"]["mobile"].stringValue
let home = user["tel"]["home"].stringValue

print("User: uid:\(uid!),uname:\(uname!),mobile:\(mobile!),home:\(home!)")
评论

全部评论(7)

回到顶部