- 浏览: 528387 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
landerson:
明显就有要求的嘛
ANDROID轻量级JSON序列化和反序列化[转] -
jimode2013:
很不错,就是需要这个方法
多个UIViewController使用addSubView,第二个 UIViewController 不响应旋转[转] -
w11h22j33:
...
[转]NSMutableArray中的自动释放对象让我郁闷了一整天 -
w11h22j33:
UILabel* label = [[UILabel a ...
Iphone开发 -
w11h22j33:
http://mobile.51cto.com/iphone- ...
获得通讯录中联系人的所有属性[转]
[转]NSXMLParser解析多层嵌套xml
- 博客分类:
- Iphone开发
As promised, here is a little How-I-did-it / How-To.
First off: I am not an experienced SAX-User.. So this approach might be packing the problem at it’s tail, but this is how DOM-Users feel comfortable with ;)
Let’s assume we want to parse the following XML:
tranist.xml
- <root>
- <schedules>
- <schedule id="0">
- <from>SourceA</from>
- <to>DestinationA</to>
- <links>
- <link id="0">
- <departure>2008-01-01 01:01</departure>
- <arrival>2008-01-01 01:02</arrival>
- <info>With food</info>
- <parts>
- <part id="0">
- <departure>2008-01-01 01:01</departure>
- <arrival>2008-01-01 01:02</arrival>
- <vehicle>Walk</vehicle>
- </part>
- <part id="1">
- <departure>2008-01-01 01:01</departure>
- <arrival>2008-01-01 01:02</arrival>
- <trackfrom>1</trackfrom>
- <trackto>2</trackto>
- <vehicle>Train</vehicle>
- </part>
- </parts>
- </link>
- <link id="1">
- ...
- </link>
- <link id="2">
- ...
- </link>
- </links>
- </schedule>
- <schedule id="1">
- ...
- </schedule>
- <schedule id="2">
- ...
- </schedule>
- </schedules>
- </root>
n human readable format, this means: We have multiple schedules with from/to etc. These schedules consist of multiple links (different connections for the same route) with departure/arrival etc. These links consist then of multiple parts/sections with various elements which are not sure to be there..
With the let’s find the element called ‘part’ - approach, you won’t get anywhere..
The Basics
So what do we want to achieve? We want a list/array of Schedules, which have the given members. On member is a list/array of Links, also consisting of the given members and a list/array of parts with the respective members.
This is also the basic idea behind my approach: for every new node-container, use a new class/object (an array will also work, but it’s kinda crap..)
Now we have a Schedule class, a Link class and a Part class.
This is an example of the Link class interface:
Link.h
- #import "Part.h"
- @interface Link : NSObject {
- NSString *departure;
- NSString *arrival;
- NSString *info;
- NSMutableArray *parts;
- }
- @property (nonatomic, retain) NSString *departure;
- @property (nonatomic, retain) NSString *arrival;
- @property (nonatomic, retain) NSString *info;
- @property (readonly, retain) NSMutableArray *parts;
- - (void)addPart:(Part *)part;
- @end
We use an accessor method for the parts, because it just feels better when dealing with arrays. (Instead of later using [foo.myArray addObject:..] we have [foo addMe:..])
Also we make it easier for us, using retain properties..
The Parser setup
A short introduction into SAX:
The parsing goes node by node and is not nesting-sensitive. That means that first we get root, then schedules, then schedule, then from, then to, then links, then link, then departure etc. As soon as the parser returns you the node for example, you don’t know anymore in what schedule you were. As long as you have a clearly defined structure where always every element must be present, you could do this using a counter, but as soon as you have multiple nodes with no defined count, you have a problem.
What we do is known as recursive parsing. What does this mean? We implement some kind of memory.
In our parser, we have 4 members and 1 method (to make actual use of the parser..):
- @property (nonatomic, retain) NSMutableString *currentProperty;
- @property (nonatomic, retain) Schedule *currentSchedule;
- @property (nonatomic, retain) Link *currentLink;
- @property (nonatomic, retain) Part *currentPart;
- @property (nonatomic, readonly) NSMutableArray *schedules;
- - (void)parseScheduleData:(NSData *)data parseError:(NSError **)error;
(Yes, this needs to be a NSMutableString..)
Your parseScheduleData method should look similar to the following:
arseJourneyData
- - (void)parseJourneyData:(NSData *)data parseError:(NSError **)err {
- NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
- self.schedules = [[NSMutableArray alloc] init]; // Create our scheduler list
- [parser setDelegate:self]; // The parser calls methods in this class
- [parser setShouldProcessNamespaces:NO]; // We don't care about namespaces
- [parser setShouldReportNamespacePrefixes:NO]; //
- [parser setShouldResolveExternalEntities:NO]; // We just want data, no other stuff
- [parser parse]; // Parse that data..
- if (err && [parser parserError]) {
- *err = [parser parserError];
- }
- [parser release];
- }
Now we need those delegate methods.
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
This function is called by the parser, when it reads something between nodes. (Text that is..) Like with blah it would read “blah”. It is possible, that this method is called multiple times in one node. As you will see later, we define the property “currentProperty” only if we find a node, we care about. That’s why we test it against this property to make sure, that we need this property. This will then look something like this:
Parser
- - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
- {
- if (self.currentProperty) {
- [currentProperty appendString:string];
- }
- }
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
This is called, when the parser finds an opening element. In this case, we have a few cases, we need to distinguish. These are:
It’s standard property in the schedule (like <form> etc.) or it’s a deeper nested node (like <links>), the same for all the other nodes.
How to? We define, that we only set a member, if we are in that node. That means, only when we have entered a <part>, then currentPart is set, otherwise it’s nil. The same with the others.
We do then need to check them in reverse order of their nesting level.. Why? Because if we would check for currentLink before currentPart, currentLink would also evaluate to YES/True and hence we will have a problem if their are elements with the same name. If we aren’t in any node, then there is probably a new main node comming -> in the else..
When we hit a nested node, we need to allocate the respective member of our class, so we can use it when the parser gets deeper into it.
This will look like this:
Parser
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
- if (qName) {
- elementName = qName;
- }
- if (self.currentPart) { // Are we in a
- // Check for standard nodes
- if ([elementName isEqualToString:@"departure"] || [elementName isEqualToString:@"arrival"] || [elementName isEqualToString:@"vehicle"] || [elementName isEqualToString:@"trackfrom"] || [elementName isEqualToString:@"trackto"] ) {
- self.currentProperty = [NSMutableString string];
- }
- } else if (self.currentLink) { // Are we in a
- // Check for standard nodes
- if ([elementName isEqualToString:@"departure"] || [elementName isEqualToString:@"arrival"] || [elementName isEqualToString:@"info"]) {
- self.currentProperty = [NSMutableString string];
- // Check for deeper nested node
- } else if ([elementName isEqualToString:@"part"]) {
- self.currentPart = [[Part alloc] init]; // Create the element
- }
- } else if (self.currentSchedule) { // Are we in a ?
- // Check for standard nodes
- if ([elementName isEqualToString:@"from"] || [elementName isEqualToString:@"to"]) {
- self.currentProperty = [NSMutableString string];
- // Check for deeper nested node
- } else if ([elementName isEqualToString:@"link"]) {
- self.currentLink = [[Link alloc] init]; // Create the element
- }
- } else { // We are outside of everything, so we need a
- // Check for deeper nested node
- if ([elementName isEqualToString:@"schedule"]) {
- self.currentSchedule = [[Schedule alloc] init];
- }
- }
- }
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
Basically, the same things apply as for didStartElement above. This time, we need to clean things up and assign them if they are set :) This is a bit a pitty, since it’s a lot of code.. *(for not so much)
It’s the same checker-structure..
If we are in a deeper nested node (like <Link>) and we hit an ending element of that nested node (like </Link>), Then we need to add this element to the parent (like <Schedule>) and set it to nil
See yourself:
arser
- - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
- if (qName) {
- elementName = qName;
- }
- if (self.currentPart) { // Are we in a
- // Check for standard nodes
- if ([elementName isEqualToString:@"departure"]) {
- self.currentPart.departure = self.currentProperty;
- } else if ([elementName isEqualToString:@"arrival"]) {
- self.currentPart.arrival = self.currentProperty;
- } else if ([elementName isEqualToString:@"vehicle"]) {
- self.currentPart.vehicle = self.currentProperty;
- } else if ([elementName isEqualToString:@"trackfrom"]) {
- self.currentPart.trackfrom = self.currentProperty;
- } else if ([elementName isEqualToString:@"trackto"]) {
- self.currentPart.trackto = self.currentProperty;
- // Are we at the end?
- } else if ([elementName isEqualToString:@"part"]) {
- [currentLink addPart:self.currentPart]; // Add to parent
- self.currentPart = nil; // Set nil
- }
- } else if (self.currentLink) { // Are we in a
- // Check for standard nodes
- if ([elementName isEqualToString:@"departure"]) {
- self.currentLink.departure = self.currentProperty;
- } else if ([elementName isEqualToString:@"arrival"]) {
- self.currentLink.arrival = self.currentProperty;
- } else if ([elementName isEqualToString:@"info"]) {
- self.currentLink.info = self.currentProperty;
- // Are we at the end?
- } else if ([elementName isEqualToString:@"link"]) {
- [currentSchedule addPart:self.currentLink]; // Add to parent
- self.currentLink = nil; // Set nil
- }
- } else if (self.currentSchedule) { // Are we in a ?
- // Check for standard nodes
- if ([elementName isEqualToString:@"from"]) {
- self.currentSchedule.from = self.currentProperty;
- } else if ([elementName isEqualToString:@"to"]) {
- self.currentSchedule.to = self.currentProperty;
- // Are we at the end?
- } else if ([elementName isEqualToString:@"schedule"]) { // Corrected thanks to Muhammad Ishaq
- [schedules addObject:self.currentSchedule]; // Add to the result node
- self.currentSchedule = nil; // Set nil
- }
- }
- // We reset the currentProperty, for the next textnodes..
- self.currentProperty = nil;
- }
Finally..
Well, that’s it. You can expand / shrink this principle as you like. You can also add a maxElements counter, like in the SeismicXML example of the iPhone SDK to get only a certain number of elements. You can abort the parser with [parser abortParsing]; It is important, that you don’t abort while in a deeper nested node, because this could lead to inconsistencies. You will need to skip them..
Please note, that I wrote this, while watching TV, so you may need to fix some syntax errors ;) But I hope you get the idea..
from:http://codesofa.com/blog/archive/2008/07/23/make-nsxmlparser-your-friend.html
发表评论
-
iOS App性能优化
2014-01-03 11:23 1653http://www.hrchen.com/2013/05/ ... -
iOS多线程编程Part 3/3 - GCD
2014-01-03 11:21 1597http://www.hrchen.com/2013/07/ ... -
iOS多线程编程Part 2/3 - NSOperation
2014-01-03 11:20 4493http://www.hrchen.com/2013/06/ ... -
iOS多线程编程Part 1/3 - NSThread & Run Loop
2014-01-03 11:17 7048http://www.hrchen.com/2013/06/ ... -
iOS移动网络环境调优那些事[转]
2014-01-02 17:10 2666http://xiangwangfeng.com/201 ... -
生成APNS Service证书的步骤[转]
2013-05-23 09:19 5643要进行推送服务的第一件事就是获取推送证书。它用来对你通过SS ... -
xcode 环境,多工程联编设置【转】
2013-02-28 21:59 8887http://blog.csdn.net/vienna_zj ... -
干掉你程序中的僵尸代码【转】
2012-12-22 11:05 931随着万圣节越来越流行,我感觉有必要跟大家讨论一下一个 ... -
一个文本框搞定信用卡相关信息的输入[转]
2012-12-22 11:03 1096http://beforweb.com/node/134 ... -
【转】深度技术分析“为什么ios比android流畅”
2012-09-23 19:41 1406原文 Andorid更新了一个版本又一个版本,硬 ... -
Iphone开发
2012-09-17 22:46 11671. NSClassFromString 这个方法 ... -
HowTo: Install iPhone SDK 2.0 – 3.1 for XCode 3.2[转]
2012-09-06 09:00 1181原文链接 So… you’ve installe ... -
Xcode 中设置部分文件ARC支持[转]
2012-08-03 10:57 1705ARC是什么 ARC是iOS 5推出的新功 ... -
xcode4 设置调试错误信息小结【转】
2012-07-19 14:37 1760方案1:NSZombieEnabled 先选中工程, ... -
Finding iPhone Memory Leaks: A “Leaks” Tool Tutorial【转】
2012-07-19 14:36 1239Finding iPhone Memory Lea ... -
[Cocoa]XCode的一些调试技巧【转】
2012-07-19 14:35 1174XCode 内置GDB,我们可以在命令行中使用 GDB ... -
[IPhone]如何使用Leak检查内存泄漏[转]
2012-07-19 14:34 1191简介 在IPhone程式开发中,记忆体泄漏(内存泄漏)是 ... -
获得通讯录中联系人的所有属性[转]
2012-06-21 14:04 1568获得通讯录中联系人的所有属性 ,看代码: ABAdd ... -
多个UIViewController使用addSubView,第二个 UIViewController 不响应旋转[转]
2012-06-20 23:51 16377------------------------------- ... -
shouldAutorotateToInterfaceOrientation 不触发或者不执行的问题[转]
2012-06-20 22:58 1416今天遇到一个很郁闷 ...
相关推荐
使用NSXMLParser解析XML数据的一个Demo
ios xml解析 NSXMLParser。 NSXMLParser是基于SAX的解析方式。NSXMLParser采用了委托设计模式,因此他的实现类需要采用协议并支持委托。NSXMLParser解析XML需要委托NSXMLParserDelegate实现。
NULL 博文链接:https://eric-gao.iteye.com/blog/1590902
代码例子(关于NSXMLParser 解析xml文件) xml 文件 通过NSXMLParser 解析成为数组 元素内容与属性可以获取到
xml解析的两种常用方式, DOM解析:一次性将整个XML文档加载进内存,比较适合解析小文件,例如:GDataXml解析 ...SAX解析:从根元素开始,按顺序一个元素一个元素往下解析,比较适合解析大文件,例如:NSXMLParser解析
Reachability 2.0版本,确认网络环境3G/WIFI 使用NSConnection下载数据 使用NSXMLParser解析xml文件
SHXMLParser 是一个基于 NSXMLParser 构建的易用的 XML 解析器,可转换 XML 数据到 Objective-C 对象。 标签:SHXMLParser
xmlparsing-with-nsxmlparser-tutorial 你可以找到的NSXMLParser完整XMLParsing -教程这里 。 本教程由最好的The App Guruz提供。
前言:本篇随笔介绍的是XML解析。 正文: 1、XML解析方式有2两种: DOM:一次性将整个XML数据加载进内存进行解析,比较适合解析小文件SAX:从根元素开始,按顺序... 2-3、本人基于苹果原生NSXMLParser封装好的XML解析
在iPhone开发中,XML的解析有很多选择,iOS SDK提供了NSXMLParser和libxml2两个类库,另外还有很多第三方类库可选,例如TBXML、TouchXML、KissXML、TinyXML和GDataXML。问题是应该选择哪一个呢? 解析 XML 通常有两...
通过SOAP 网络请求数据,并使用NSXMLParser解析Xml
xml转字典,采用系统NSXMLParser解析,轻轻松松一句代码完成xml转换字典操作
本程序演示了如何连接浏览器,如何获取数据并解析xml文件。
用于NSXMLParser的ReactiveCocoa扩展:一种简洁的,基于流的API,用于使用NSXMLParser解析XML。 围绕定义包装器,从而不再需要实现繁琐的委托方法。 应用所需的任何ReactiveCocoa魔术(请参阅 ): # import " ...
主要介绍了使用Swift实现iOS App中解析XML格式数据的教程,讲到了iOS中提供的NSXMLParser和NSXMLParserDelegate两个API的用法,需要的朋友可以参考下
使用iphoneSDK官方NSXMLParserDelegate做的简单xml解析,附带详细注释以及使用到的方法的详细解释,灰常适合新手,高手请留下你们的改进意见。
在Swift中实现的简单XML解析器这是什么? 这是一个受SwiftyJSON和SWXMLHash启发的XML解析器。 Foundation框架中的NSXMLParser是一种“ SAX”解析器。 它具有足够的性能,但是在Swift中实现了一点inc Simple XML ...
NSXMLParser *xmlRead = [[NSXMLParser alloc] initWithData:xmlData]; [xmlRead setDelegate:self]; [xmlRead parse]; [xmlRead release]; //get dataTemplate for (int i=0;i<[objects count]; i++) { ...
SWXML哈希 SWXMLHash是在Swift中解析XML的相对简单的方法。 如果您熟悉NSXMLParser ,则此库是围绕它的简单包装。 从概念上讲,它提供了从XML到数组字典(又名哈希)的转换。 该API从中获得了很多启发。内容要求iOS ...
概述基于Swift的RSS阅读器,可将rss feed作为xml从远程服务器加载,并使用NSXMLParser对其进行解析。 解析完成后,它将数据加载到UITableView即显示标题和日期。 当单击标题时,它将转到详细信息页面,在该页面中,...