微信号:SwiftDev

介绍:探讨移动开发技术,推荐常用框架,分享移动互联网资讯. 主要提供Swift;IOS等相关技术资讯

使用Swift原生库解析XML数据

2016-01-24 20:04 Swift开发
作者:conanwhf
地址:http://conanwhf.gitcafe.io/2015/12/15/swift_xml/

在实现文件操作的时候我意识到了一个事实:由于NSURL是可支持的路径,很多文件的操作实际上可以扩充到网络上去,比如从网上下载一个文件、拿取一些数据……想到这里,埋藏在心中很久的愿望又开始蠢蠢欲动:写一个新闻客户端!

我有一个常看的新闻网站(不要问我是哪个,要FQ),官方客户端很难用,第三方广告满天飞,严重影响心情。所以当初开始学iOS的时候就把重做一个客户端的目标放在了首位。最开始的时候想得太简单,兴冲冲地开了个project,搞了两下TableView,然后发现……对于我这种从来只写底层代码,swift/OC语法都不会的人,做这个简直是异想天开好嘛!

折腾的过程中,为了拿到数据,还跑去下了Android的官方客户端反编译,find + grep出来了人家的数据接口地址(论一个爱折腾的程序员是怎么给自己挖坑的),返回的是XML数据,连蒙带猜试了试,可以用!不过后来做UI受挫(其实不止做UI,挫败感太大),就把那份东西闲置起来了。这两天突然想起来,现在我貌似感觉有点良好,何不捡起来继续写我的客户端?

因为UI我还一窍不通,不懂怎么调试,也不懂怎么把数据显示出来,为了避免到处打印的麻烦,我选择playground。这样也就是需要用原生库,这个很简单,搜一下就有了:NSXMLParserDelegate,还有好些源码,虽然大多是OC的。

问题是有源码,我依!然!看!不!懂!怎么用!每个教程都是扔了源码上来,可没人解释清楚到底怎么运行。我研究了好一阵,终于明白了大概的机制:

自己写一个类继承NSXMLParserDelegate,就叫它XmlReader吧;并且实现一些必须的callback函数,在XmlReader的Init中调用.parse()方法,最后在new XmlReader的时候就会自动完成整个parse了

当然你也可以不把parse()放在init()中调用,而是在new了XmlReader之后强制调用,重要的是那些callback函数的实现。XmlReader中还可以声明一些变量,来保存解析过程中的状态,而具体的获得到的数据填充,也是需要你自己做的(数据结构自行定义)。

鉴于XML是一个有层级的带递归意味的数据结构,程序会层层解析下去直到结束,中间遇到的各种类似section开始、结束、拿到字符串等情况都会call一个固定的函数,将解析出来的数据作为参数传进去,而你要做的,就是实现这些callback以完成自己想要做的事。

下面是具体的代码示范。首先是定义一个新的类:

class XmlReader: NSObject, NSXMLParserDelegate {
var currentName :String? = nil
var level :Int = 0

 init(add :String ) {

    super.init()
    let url = NSURL(string:add)!
    guard let parserXML = NSXMLParser(contentsOfURL: url) else {
        return
    }

    parserXML.delegate = self
    parserXML.parse()
    }

func parserDidStartDocument(parser: NSXMLParser) {}

func parserDidEndDocument(parser: NSXMLParser){}

func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) {}

func parser(parser: NSXMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {}

func parser(parser: NSXMLParser, var foundCharacters string: String) {}

func parser(parser: NSXMLParser, parseErrorOccurred parseError: NSError) {}
}

这是一个基础部分,里面定义了一些callback但没有实现它。我把xml的地址作为参数传给了init(),并且声明了两个变量currentName和level分别用来保存当前的标签名字和递归深度。然后我们基于下面这个简单的XML文件继续完成callback部分:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<language cat="it">
    <lan id="1">
        <name>C#</name>
        <IDE>vs</IDE>
    </lan>
    <lan id="2" />
</language>

首先是文档开始和结束时:

//文档开始解析时触发,只触发一次
func parserDidStartDocument(parser: NSXMLParser) {
    print("    start")
}

//文档结束时触发,只触发一次,通常需要在这里给出一个信号告诉上层或其他人解析已经结束
func parserDidEndDocument(parser: NSXMLParser){
    print("    end")
}

然后是标签的开始和结束事件:

//遇到一个开始标签触发,elementName为当前标签,如果当前标签有属性,则字典sttributeDict不为空
func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]()) {
    self.currentName = elementName
    self.level++
    print("\(level) start, \(elementName), \(attributeDict)")
    if currentName == "language" { // 获取language属性的内容
        //print("language: \(attributeDict)")
    }
}

//遇到结束标签触发,该方法主要是做一些清理工作,在这里我修改了当前的深度
func parser(parser: NSXMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
    print("\(level) end, \(elementName)")
    self.currentName = nil
    self.level--
}

接下来是字符串值的handler

// 遇到字符串时触发
func parser(parser: NSXMLParser, var foundCharacters string: String) {
    //删除首尾的回车符和空格
    string = string.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
    if string.isEmpty {
        return
    }
    print("---- \(string)")
}

最后是Error Handler

// 文档出错时触发
func parser(parser: NSXMLParser, parseErrorOccurred parseError: NSError) {
    print(parseError)
}

以上的callback函数实现都只放了些打印信息,在实际操作中需要把这些获取的信息一一填充进自己的数据结构中。说实话是挺麻烦的,难怪都说XML快要被JSON全面取代(其实对于曾被libxml折磨过的码农来说,这已经挺好了)。全部的代码传送门:

https://github.com/conanwhf/swiftplayground/tree/master/Play.playground/Pages/GetXML.xcplaygroundpage

有人用微信聊天,有人却在微信中学习,成长。下面是2016最HOT公众号,赶快试试新的关注方法吧!


关注方式


★长按二维码,选择“识别图中二维码”进行关注。


iOS开发

微信号:iOSDevTip


▲长按二维码“识别.”关注

简介:每天学习一个小技巧,让学习iOS开发更简单!


猿圈

微信号:CodePush


▲长按二维码“识别.”关注

简介:为你揭秘地球上神秘的程序猿的生活圈!



Android开发精选

微信号:AndroidPush


▲长按二维码“识别.”关注

简介:定期翻译、发布国外Android优质的技术文章,开源库。


Swift开发

微信号:SwiftDev


▲长按二维码“识别.”关注

简介:Apple开源的最新官方语言Swift,更简单,更灵活,也更有趣!


APP开发者

微信号:aswifter


▲长按二维码“识别.”关注

简介:分享iOS开发、Swift开发、Android开发和移动互联网技术。


Python开发

微信号:PythonPush


▲长按二维码“识别.”关注

简介:人生苦短,我用Python。



 
Swift开发 更多文章 协同写作的力量——中国开发者9天完成《Swift语言》中文版 程序员眼中的苹果Swift语言:简单 易学 高效 使用Dollar.$wift简化代码编写 Swift版知乎日报 Swift-PM25 一个基于Swift实现的PM2.5查询示例
猜您喜欢 开源大数据在Facebook与Dropbox的实践 大数据时代与精准运维 PM必看:《iOS 人机界面准则》&amp;《Material design》 程序员在法国 | 程序员都有哪些怪癖 【App自动化测试公开课】光荣之路和金阳光联合推出『MonkeyRunner』周四约