Swift - 解析XML格式数据(分别使用GDataXML和KissXML)
作者:hangge | 2015-03-06 13:58
在做一些应用的时候经常需要用到XML解析,比如获取Web Service数据,读取RSS新闻或者博客数据源。下面演示了两个非常方便高效的XML库在Swift里的调用方法。
一,使用GDataXML(这个是google出品的)
1,使用方法
2,DDXML获取子元素:forName(name: String)与elements(forName: String)
forName(name: String) 是根据name取同名的子结点,如果有多个 ,就只取第一个。
elements(forName: String) 是把所有的同名子节点都返回。
比如把上面XML数据文件users.xml每个人都添加多个mobile节点:
3,扩展DDXML方便使用
假设需要被解析的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)
二,使用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)
import UIKit
struct uuui {
var uid: String?
var uname: String?
var mobile: String?
var home: String?
}
class main3ViewController: UIViewController,XMLParserDelegate {
var users:[uuui] = []
//........
航哥,我用了Xcode自带的XMLParserDelegate 方法,感觉会方便些,您看下,Xcode9.2做的,解析的也是这个xml
站长回复:谢谢你的提醒,使用原生库确实也有省事的地方,至少不用引入其他第三方库了。后面我考虑再写篇XMLParserDelegate的文章作为补充,最后再次感谢你提供的建议和代码。
我的XML是这样的:
<?xml version="1.0" encoding="utf-8"?>
<地球>
<亚洲>
<国家>
<name>中国</name>
<desc>中国是一个古代文明的国家</desc>
</国家>
<国家>
<name>韩国</name>
<desc>韩国的女人很丑</desc>
</国家>
<国家>
<name>朝鲜</name>
<desc>朝鲜我不知道什么样的国家</desc>
</国家>
</亚洲>
<欧洲>
<国家>
<name>英国</name>
<desc>英国是一个古代文明的国家</desc>
</国家>
<国家>
<name>德国</name>
<desc>德国战车队</desc>
</国家>
</欧洲>
</地球>
目前我遇到的问题是:
1.比如:亚洲有三个国家,但是我解析的时候它只显示整个亚洲一个,没有显示不到里面的国家,欧洲也如此。怎么解决洲和对应国家的关系?
站长回复:不确定你代码是怎么写的。我这里以使用KissXML解析为例,如果要解析出亚洲下所有的国家,代码是:
博主,我要用XML实现这样一个功能:第一页要呈现亚洲,欧洲等各州的国家数,然后选择一个洲,第二页要呈现对应这个洲的国家。然后选择一个国家,第三页要呈现选择国家的基本情况。能不能帮我一个忙。
站长回复:你可以做三个VC页面,第一个页面表格加载xml第一层的数据(各个州),点击单元格跳到第二个页面。第二个页面表格加载对应洲节点下数据(各个国家),点击单元格跳到第最后一个页面。也就是把对应国家的节点数据加载显示即可。
请问一下怎么把objective-c那个引用计数改为no之后报错
SDWebImage/SDWebImagePrefetcher.m:28:1: @synthesize of 'weak' property is only allowed in ARC or GC mode
站长回复:这个我也不知道什么原因,我这边测试了下没问题的。
请问下载的文件里libxml2.2.dylib怎么都不见了的啊
站长回复:libxml2.2.dylib现在改成libxml2.2.tbd了,文章相关代码已修改。
站长,能把DDXML改一下向swiftyjson那样用么?
let mobile = (telElement.elementForName("mobile") as DDXMLElement).stringValue()
改成
let mobile = user["tel"]["mobile"] as string
这样
站长回复:想要用这种方式应该是没问题的,只要扩展下就好了。我周末抽空写个。
(2015-12-05:已给DDXMLElement添加扩展,文章已更新,你可以看下。)
很好用
站长回复:^_^