iPhone的XML库
XML解析与内存占用
libxml2 vs NSXMLParser
NSXMLParser的例子
libxml2的例子
项目中添加libxml
libxml中的SAX解析器
使用DOM解析
Google Data APIs
TouchXML
KissXML
iPhone开发技巧之网络篇(1)--- 解析XML
XML解析与内存占用
libxml2 vs NSXMLParser
NSXMLParser的例子
libxml2的例子
项目中添加libxml
libxml中的SAX解析器
使用DOM解析
Google Data APIs
TouchXML
KissXML
iPhone开发技巧之网络篇(1)--- 解析XML
开发 iPhone 上的网络应用程序的时候时常需要解析XML文档,比如web应用中的SOAP,REST,RSS信息等都是以XML为基础的。掌握XML解析的技术是很重要的。这里我将为大家介绍一下iPhone下解析XML的几种方法,并比较其性能。
iPhone的XML库
iPhone中标准的XML解析库有两个,分贝是libxml2和NSXMLParser。
iPhone中标准的XML解析库有两个,分贝是libxml2和NSXMLParser。
libxml2由Gnome项目开发、由于是MIT的开放协议,已经移植到许多的平台,在iPhone上也能使用。
libxml2的特点是比较快。另外作为最基本的XML解析器,提供SAX和DOM解析。并且它对应的XML标准最多,比如名称空间、XPath、XPointer、HTML、XInclude、XSLT、XML Schema、Relax NG等。另外它是用C语言写的,比较高速。
NSXMLParser是Cocoa中内含的XML解析器。它只提供了SAX解析的功能。因为是Cocoa的一部分、并且API是Objective-C的,所以与Mac系统兼容性强,使用也相对简单。
XML解析与内存占用
由于iPhone也是一种嵌入式设备,所以与其他的嵌入式设备一样,同样有内存,CPU等资源占用问题。所以在选择代码库的时候需要考虑性能与内存占用的问题。
由于iPhone也是一种嵌入式设备,所以与其他的嵌入式设备一样,同样有内存,CPU等资源占用问题。所以在选择代码库的时候需要考虑性能与内存占用的问题。
一般XML的解析器有SAX解析和DOM解析两种方式、相比之下SAX比较小巧精干,耗费的内存小。这是因为其设计思想与DOM完全不一样,一边得到数据一边解析,由回调的方式通知得到的数据,没有了DOM树的概念。
现在的iPhone 3G搭载的RAM是128MB(3GS是256MB)。其中有iPhone OS本身使用的、还有根据用于使用情况不同,比如MP3,邮件,Safari等常驻程序等。基本上自己的程序可使用的内存大小是10MB左右的空间。
开发XML解析程序的时候,需要注意到XML文件一般都比较大,如果使用DOM的话,消费的内存量肯定很多。所以在iPhone中上面这两种解析器只能使用SAX的解析方式。DOM方式只能在模拟器上使用(比如NSXMLDocument类),放到实际设备上就不管用了。(不过,在下面的章节中我将介绍一种使用DOM的第三方方法,对于小的XML文档还是值得一用的。)
libxml2 vs NSXMLParser
一般是使用libxml2的SAX解析器呢,还是使用NSXMLParser能,我们通过下面的SDK中附属的例子XMLPerformance来做个测试。
一般是使用libxml2的SAX解析器呢,还是使用NSXMLParser能,我们通过下面的SDK中附属的例子XMLPerformance来做个测试。
相同的XML文档由网络下载,然后解析,比较的结果如下 :
下载用时 解析用时 合计
NSXMLParser 1.419s 5.525s 7.134s
libxml2 2.520s 2.247s 2.646s
NSXMLParser 1.419s 5.525s 7.134s
libxml2 2.520s 2.247s 2.646s
可以看到,libxml2比NSXMLParser快得多。这与它们处理的方式有些关系,NSXMLParser中调用SAX API的时候,参数是作为字符串传递的,需要先转换为NSString或者是NSDictionary对象,并且它不像libxml2那样是一边下载一边解析,需要整个文件下载完了才开始解析。所以说建议一般使用libxml2。
NSXMLParser的例子
解析的XML代码例子如下:
<?xml version="1.0" encoding="UTF-8"?>
<users>
<user name="hoge" age="20" />
<user name="fuga" age="30" />
</users>
解析的XML代码例子如下:
<?xml version="1.0" encoding="UTF-8"?>
<users>
<user name="hoge" age="20" />
<user name="fuga" age="30" />
</users>
代码如下:
static NSString *feedURLString = @"
http://www.yifeiyang.net/test/test.xml";
- (void)parserDidStartDocument:(NSXMLParser *)parser
{
// 解析开始时的处理
}
{
// 解析开始时的处理
}
- (void)parseXMLFileAtURL:(NSURL *)URL parseError:(NSError **)error
{
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:URL];
[parser setDelegate:self];
[parser setShouldProcessNamespaces:NO];
[parser setShouldReportNamespacePrefixes:NO];
[parser setShouldResolveExternalEntities:NO];
[parser parse];
NSError *parseError = [parser parserError];
if (parseError && error) {
*error = parseError;
}
[parser release];
}
{
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:URL];
[parser setDelegate:self];
[parser setShouldProcessNamespaces:NO];
[parser setShouldReportNamespacePrefixes:NO];
[parser setShouldResolveExternalEntities:NO];
[parser parse];
NSError *parseError = [parser parserError];
if (parseError && error) {
*error = parseError;
}
[parser release];
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
// 元素开始句柄
if (qName) {
elementName = qName;
}
if ([elementName isEqualToString:@"user"]) {
// 输出属性值
NSLog(@"Name is %@ , Age is %@", [attributeDict objectForKey:@"name"], [attributeDict objectForKey:@"age"]);
}
}
{
// 元素开始句柄
if (qName) {
elementName = qName;
}
if ([elementName isEqualToString:@"user"]) {
// 输出属性值
NSLog(@"Name is %@ , Age is %@", [attributeDict objectForKey:@"name"], [attributeDict objectForKey:@"age"]);
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
// 元素终了句柄
if (qName) {
elementName = qName;
}
}
{
// 元素终了句柄
if (qName) {
elementName = qName;
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
// 取得元素的text
}
{
// 取得元素的text
}
NSError *parseError = nil;
[self parseXMLFileAtURL:[NSURL URLWithString:feedURLString] parseError:&parseError];
[self parseXMLFileAtURL:[NSURL URLWithString:feedURLString] parseError:&parseError];
实际使用的时候除最后两行以外,所有的当如一个类中,最后两个是启动该类的代码。
libxml2的例子
项目中添加libxml
首先需要将libxml添加到你的工程项目中。
项目中添加libxml
首先需要将libxml添加到你的工程项目中。
我们知道,当向项目中添加外部库的时候,如果是程序框架的,比如UIKit.framework,Foundation.framework等放在Sysytem/Library/Frameworks 目录下。SDK放在 /Developer/Platforms/iPhoneOS.platform/Developer/SDKs 目录下。
但是由于libxml是UNIX的库、位于SDK文件夹的usr/lib下。头文件位于 usr/include 下。
在 libxml 目录下将 libxml2.2.dylib(或更高版本)添加到项目中。将头文件路径 usr/include/libxml2 也添加到包含路径中。
以下是libxml2.2.dylib的路径
/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS${VER}sdk/usr/lib/libxml2.2.dylib
以下是头文件的路径
/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS${VER}.sdk/usr
/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS${VER}sdk/usr/lib/libxml2.2.dylib
以下是头文件的路径
/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS${VER}.sdk/usr
libxml中的SAX解析器
用过SAX解析器的朋友都知道,SAX就是事先登录一些处理函数,当XML解析到属性或要素的时候,回调登录的处理函数。
以下是一个例子,DownloadOperation实现网络文件的下载,同时交给libxml2的SAX解析器处理:
: { * _request; * _connection; xmlParserCtxtPtr _parserContext; _isExecuting, _isFinished; _isChannel, _isItem; * _channel; * _currentItem; * _currentCharacters; } @property (readonly) * ; - ():(*); |
首先、#import了libxml/tree.h头文件。然后声明了一个 xmlParserCtxtPtr 变量作为解析器的实例。
() - ():( *) :( *) :( *) :() :( **) :() :() :( **); - ():( *) :( *) :( *); - ():( *) :(); ( * , * , * , * , , ** , , , ** ) { [(*)ctx startElementLocalName:localname prefix:prefix URI:URI nb_namespaces:nb_namespaces namespaces:namespaces nb_attributes:nb_attributes nb_defaulted:nb_defaulted attributes:attributes]; } ( * , * , * , * ) { [(*)ctx endElementLocalName:localname prefix:prefix URI:URI]; } ( * , * , ) { [(*)ctx charactersFound:ch len:len]; } _saxHandlerStruct = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, charactersFoundHandler, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, XML_SAX2_MAGIC, NULL, startElementHandler, endElementHandler, NULL, }; = _channel; mark -- Initialize -- + ():(*) { ([key isEqualToString:@] || [key isEqualToString:@]) { ; } [ automaticallyNotifiesObserversForKey:key]; } - ():(*) { (![ init]) { ; } _request = [request retain]; _isExecuting = ; _isFinished = ; _channel = [[ dictionary] retain]; [_channel setObject:[ array] forKey:@]; _currentItem = ; ; } - () { [_request release], _request = ; [_connection cancel]; [_connection release], _connection = ; [_channel release], _channel = ; [_currentCharacters release], _currentCharacters = ; [ dealloc]; } mark -- Operating -- - () { ; } - () { _isExecuting; } - () { _isFinished; } - () { (![ isCancelled]) { _parserContext = xmlCreatePushParserCtxt(&_saxHandlerStruct, , NULL, 0, NULL); [ setValue:[ numberWithBool:] forKey:@]; _isChannel = ; _isItem = ; [ connectionWithRequest:_request delegate:]; } } - () { (_parserContext) { xmlFreeParserCtxt(_parserContext), _parserContext = NULL; } [_connection cancel], _connection = ; [ setValue:[ numberWithBool:] forKey:@]; [ setValue:[ numberWithBool:] forKey:@]; [ cancel]; } mark -- NSURLConnection delegate -- - ():(*) :(*) { xmlParseChunk(_parserContext, ( *)[data bytes], [data length], 0); } - ():(*) { xmlParseChunk(_parserContext, NULL, 0, 1); (_parserContext) { xmlFreeParserCtxt(_parserContext), _parserContext = NULL; } _connection = ; [ setValue:[ numberWithBool:] forKey:@]; [ setValue:[ numberWithBool:] forKey:@]; } - ():(*) :(*) { (_parserContext) { xmlFreeParserCtxt(_parserContext), _parserContext = NULL; } _connection = ; [ setValue:[ numberWithBool:] forKey:@]; [ setValue:[ numberWithBool:] forKey:@]; } mark -- libxml handler -- - ():( *) :( *) :( *) :() :( **) :() :() :( **) { (strncmp((*)localname, , sizeof()) == 0) { _isChannel = ; ; } (strncmp((*)localname, , sizeof()) == 0) { _isItem = ; _currentItem = [ dictionary]; [[_channel objectForKey:@] addObject:_currentItem]; ; } (strncmp((*)localname, , sizeof()) == 0 || strncmp((*)localname, , sizeof()) == 0 || strncmp((*)localname, , sizeof()) == 0) { [_currentCharacters release], _currentCharacters = ; _currentCharacters = [[ string] retain]; } } - ():( *) :( *) :( *) { (strncmp((*)localname, , sizeof()) == 0) { _isChannel = ; ; } (strncmp((*)localname, , sizeof()) == 0) { _isItem = ; _currentItem = ; ; } (strncmp((*)localname, , sizeof()) == 0 || strncmp((*)localname, , sizeof()) == 0 || strncmp((*)localname, , sizeof()) == 0) { * ; key = [ stringWithCString:(*)localname encoding:NSUTF8StringEncoding]; * = ; (_isItem) { dict = _currentItem; } (_isChannel) { dict = _channel; } [dict setObject:_currentCharacters forKey:key]; [_currentCharacters release], _currentCharacters = ; } } - ():( *) :() { (_currentCharacters) { * ; string = [[ alloc] initWithBytes:ch length:len encoding:NSUTF8StringEncoding]; [_currentCharacters appendString:string]; [string release]; } } |
连接开始的时候(start函数)使用 xmlCreatePushParserCtxt 创建解析器实例,这里注意第二个参数,将DownloadOperation 的实例传到解析器内,这个正是回调函数中的第一个参数 — 作为回调函数的句柄调用类成员函数(当然,不使用实例方法,将回调函数设置成类方法也是可行的。但是当你使用到DownloadOperation中的成员等会有些不便,所以从OO的角度出发,还是传递回调函数的对象实例为佳)。
开始下载的时候,因为数据是顺序得到的,所以一边下载,一边用 xmlParseChunk 传递给解析器。
libxml的SAX句柄函数在xmlSAXHandler结构中定义。这个构造体内有30多个句柄定义,一般我们只需要登录其中几个就够了。比如例子中的 startElementNsSAX2Func、endElementNsSAX2Func、charactersSAXFunc 等,他们的定义如下:
最后,因为用SAX解析,需要知道当前解析的位置,所以标记参数需要合理的使用。
使用DOM解析
上面我们已经介绍了,iPhone 中的XML解析器都是SAX的,如果仅仅对于比较小的XML文档,或者说想得到DOM树结构的XML文档来说,使用DOM解析还是有一定价值的(比如针对简单的SOAP,REST文档解析等)。
Google Data APIs
这里介绍一种使用第三方类库的方法,具体见
这里。其实说是第三方类库,其实还是使用了libxml2,所以前期库文件和头文件的设置与上面libxml2是一致的。并将 -lxml2 加到link的设置中。
使用的时候,先从
这里下载并解冻 Google Data APIs Objective-C Client Library,然后将下面解开的文件拷贝到项目中去。
GDataXMLNode.h GDataXMLNode.m GDataDefines.h GDataTargetNamespace.h |
解析的例子如下:
<users> <user id=> <name>azalea</name> <email>azalea@azalea.net</email> <address country=>Hokkaido</address> </user> <user id=> <name>Baka.K.El.Doglla</name> <email>unknown@unknown.net</email> <address country=>Doglla</address> </user> </users> |
下面就是使用方法了,DOM的API使用起来还是感觉便利些:
- ():( *) { * = [[ mainBundle] pathForResource:@ ofType:@]; * = [ stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:]; NSLog(@,[path retainCount],[fileText retainCount]); * ; * = [[ alloc] initWithXMLString:fileText options:0 error:&error]; * = [document rootElement]; = 0; * = [rootNode nodesForXPath:@ error:&error]; (* userList) { ([node stringValue]); } [window addSubview:[navigationController view]]; [window makeKeyAndVisible]; [document release]; } |
TouchXML
TouchXML与上面的Google Data的XML解析器类似,也是基于libxml2的一款第三方DOM解析器。设置是一样的。
下面开一个例子(从网上摘抄的):
mark - mark NewCateBooksViewController + (*) : (*) { (0 == [xmlString length]) ; * = [[[ alloc] init] autorelease]; * = [[ alloc] initWithXMLString: xmlString options: 0 error: ]; * = ; resultNodes = [doc nodesForXPath:@ error:]; ([resultNodes count]) { * = [resultNodes lastObject]; (rootElement) { * _bookElements = [rootElement elementsForName:@]; (* _bookElement _bookElements) { * = [[ alloc] init]; * = [_bookElement elementsForName:@]; ([idElements_ count]) bean.book_id=[[idElements_ lastObject]stringValue]; * = [_bookElement elementsForName:@]; ([nameElements count]) bean.book_name = [[nameElements lastObject] stringValue]; * = [_bookElement elementsForName:@]; ([authorElements count]) bean.book_author= [[authorElements lastObject] stringValue]; * = [_bookElement elementsForName:@]; ([urlElements count]) bean.book_pic_url= [[urlElements lastObject] stringValue]; * = [_bookElement elementsForName:@]; ([descripElements count]) bean.book_description = [[descripElements lastObject] stringValue]; * = [_bookElement elementsForName:@]; ([sizeElements count]) bean.book_size = [[sizeElements lastObject] stringValue]; [items addObject: bean]; [bean release]; } } } [doc release]; items; } mark - -():( *) { *; *; *; *; * =[ requestWithURL:[NSURL URLWithString:Url]]; [request setHTTPMethod:@]; dataReply = [ sendSynchronousRequest:request returningResponse:&response error:&error]; (dataReply==&&error!=) { ; } { stringReply = [[ alloc]initWithData:dataReply encoding:NSUTF8StringEncoding]; [bookArray addObjectsFromArray:[xmlTry parseBooks:stringReply]]; [stringReply release]; } } |
KissXML
KissXML据说速度比 TouchXML 快些,暂时还没有试过,用兴趣的朋友可以试试。例子如下:
* = [[[ alloc] initWithData:data options:0 error:&error] autorelease]; * = [doc rootElement]; [root addNamespace:[ namespaceWithName:@ stringValue:@]]; [root addNamespace:[ namespaceWithName:@ stringValue:@]]; [root addNamespace:[ namespaceWithName:@ stringValue:@]]; [root addNamespace:[ namespaceWithName:@ stringValue:@]]; * = [root nodesForXPath:@ error:&error]; * = [root nodesForXPath:@ error:&error]; * = [root nodesForXPath:@ error:&error]; * = [root nodesForXPath:@ error:&error]; * = [root nodesForXPath:@ error:&error]; * = [root nodesForXPath:@ error:&error]; ( = 0; i < [feedTitles count]; i++) { * = [[[ alloc] init] autorelease]; [result setObject:[[feedIds objectAtIndex:i] stringValue] forKey:@]; [result setObject:[[feedTitles objectAtIndex:i] stringValue] forKey:@]; [result setObject:[[entryTitles objectAtIndex:i] stringValue] forKey:@]; [result setObject:[[contents objectAtIndex:i] stringValue] forKey:@]; [result setObject:[[publisheds objectAtIndex:i] stringValue] forKey:@]; [.objects addObject:result]; } |
说到XML不得不提WEB应用中最常见的几种通讯规范:SOAP,XML-RPC,WSDL等,他们都是基于XML协定的。在下一节中我将介绍几种处理web应用的程序库。
相关文章
- iPhone开发技巧之发布篇(6)--- 不需Developper认证的真机调试方法 - (2011-12-25)
- iPhone开发技巧之环境篇(11) --- 让Xcode对应多个版本的iOS SDK - (2011-12-03)
- iPhone开发技巧之发布篇(5)--- 在程序中添加广告 - (2011-11-20)
- iPhone开发技巧之环境篇(10)--- 在控制台调试iPhone应用程序 - (2011-11-13)
- iPhone开发技巧之调试篇(3)--- 程序Crash后的调试技巧 - (2011-11-06)
- iPhone开发技巧之环境篇(9)--- Xcode中的注释 - (2011-01-12)
- iPhone开发技巧之数据篇(2)--- iPhone程序中的加密处理 - (2011-01-10)
- iPhone开发技巧之发布篇(4)--- 使用 Ad Hoc 发布自己的应用程序 - (2010-07-22)
- iPhone开发技巧之发布篇(3)--- 你的程序被拒了吗? - (2010-07-19)
- iPhone开发技巧之发布篇(2)--- 税务相关手续 - (2010-07-16)
- iPhone开发技巧之发布篇(1)--- 登录银行信息 - (2010-07-12)
- iPhone开发技巧之工具篇(4)--- 使用afconvert转换WAV文件 - (2010-06-24)
- iPhone开发技巧之私有API(8)--- UIApplication - (2010-06-18)
- iPhone开发技巧之私有API(7)--- 用UIWebView访问BASIC认证的页面 - (2010-06-16)
- iPhone开发技巧之私有API(6)--- 设置UIWebView中的User-Agent - (2010-06-14)
- iPhone开发技巧之私有API(5)--- UISegmentedControl - (2010-06-11)
- iPhone开发技巧之私有API(4)--- UIBarButtonItem - (2010-06-09)
- iPhone开发技巧之私有API(3)--- UIButton - (2010-06-07)
- iPhone开发技巧之数据篇(1)--- 使用正则表达式 - (2010-06-04)
- iPhone开发技巧之私有API(2)--- UITableView - (2010-06-01)
- iPhone开发技巧之私有API(1) --- 设备相关信息 - (2010-05-28)
- iPhone开发技巧之网络篇(5)--- 使用libcurl连接https服务器 - (2010-05-17)
- iPhone开发技巧之网络篇(4)--- 确认网络环境 3G/WIFI - (2010-05-14)
- iPhone开发技巧之网络篇(3)--- 使用NSOperation建立多任务网络连接 - (2010-05-12)
- iPhone开发技巧之网络篇(2)--- Web服务 - (2010-04-20)
转载于:https://blog.51cto.com/zhaohaiyang/756081