如果需要了解关于文档对象模型和XML的介绍,参看 http://www.cnblogs.com/xinchrome/p/4890723.html
读取XML
上代码:
NSFileHandle *file = [NSFileHandle fileHandleForReadingAtPath:name]; NSData *data = [file readDataToEndOfFile]; [file closeFile]; //create xml parser NSXMLParser *parser; parser = [[NSXMLParser alloc] initWithData:data]; [parser setShouldProcessNamespaces:NO]; [parser setShouldReportNamespacePrefixes:NO]; [parser setShouldResolveExternalEntities:NO]; [parser setDelegate:self]; //start parse [parser parse];
我们要把文件读取到NSData中,有两种方式,一种是 [[NSData alloc]initWithContentsOfFile:path],另一种是利用文件句柄直接返回一个NSData对象;然后我们用一个parser对象解析data中的数据。
iOS中的XML解析与java语言使用的方式不一样,必须要使用代理的方式,上代码:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict { NSLog(@"=======start element:%@=========", elementName); if ([elementName isEqualToString:@"skin"]) { NSLog(@"skin name=%@", [attributeDict objectForKey:@"name"]); } else if ([elementName isEqualToString:@"string"]) { NSLog(@"string name= %@ id=%@", [attributeDict objectForKey:@"name"], [attributeDict objectForKey:@"id"]); } }
解析文档对象模型
解析 XML 通常有两种方式:DOM 和 SAX,iOS SDK提供了NSXMLParser和libxml2两个类库:
DOM解析XML时,读入整个XML文档并构建一个驻留内存的树结构(节点树),通过遍历树结构可以检索任意XML节点,读取它的属性和值。而且通常情况下,可以借助XPath,直接查询XML节点。
SAX解析XML,是基于事件通知的模式,一边读取XML文档一边处理,不必等整个文档加载完之后才采取操作,当在读取解析过程中遇到需要处理的对象,会发出通知对其进行处理。
特别需要注意的是,在SAX方式中,parser对象只负责解析并发送消息给代理,不会构建文档树!树必须由代理对象自己来构造。上代码:
#import <UIKit/UIKit.h> #import "XMLElement.h" @interface ViewController : UIViewController <NSXMLParserDelegate> // 解析器对象 @property (nonatomic,strong) NSXMLParser *parser; // 根元素 @property (nonatomic,strong) XMLElement *rootElement; // 当前的元素 @property (nonatomic,strong) XMLElement *currentElementPointer; @end
#import "ViewController.h" #import "UIColor+ColorWithARGB.h" @interface ViewController () @end @implementation ViewController -(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if(self != nil){ self.title = @"XML解析"; } return self; } - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor r:0xA5 g:0xA5 b:0x52]; NSString *xmlFilePath = [[NSBundle mainBundle]pathForResource:@"person" ofType:@"xml"]; NSData *data = [[NSData alloc]initWithContentsOfFile:xmlFilePath]; self.parser = [[NSXMLParser alloc]initWithData:data]; self.parser.delegate = self; if([self.parser parse]){ NSLog(@"The XML is Parsed"); NSMutableString *str = [[NSMutableString alloc]init]; [str appendFormat:@"%@ ",self.rootElement.name]; NSMutableArray *subs = self.rootElement.subElements; for(int i = 0;i < [subs count];i++){ XMLElement *personElement = [subs objectAtIndex:i]; [str appendFormat:@" name:%@",personElement.name]; [str appendFormat:@",text:%@ ",personElement.text]; NSArray *subPersonElements = personElement.subElements; for(int j = 0;j < [subPersonElements count];j++){ XMLElement *subElement = [subPersonElements objectAtIndex:j]; [str appendFormat:@" name:%@",subElement.name]; [str appendFormat:@",text:%@ ",subElement.text]; } } NSLog(@"======解析结果:%@" ,str); }else{ NSLog(@"Failed to parse the XML"); } } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } // 文档开始 -(void)parserDidStartDocument:(NSXMLParser *)parser { self.rootElement = nil; self.currentElementPointer = nil; } // 文档结束 -(void)parserDidEndDocument:(NSXMLParser *)parser { self.currentElementPointer = nil; } // 元素开始 - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict { if(self.rootElement == nil){ self.rootElement = [[XMLElement alloc]init]; self.currentElementPointer = self.rootElement; }else{ XMLElement *newElement = [[XMLElement alloc]init]; newElement.parent = self.currentElementPointer; [self.currentElementPointer.subElements addObject:newElement]; self.currentElementPointer = newElement; } self.currentElementPointer.name = elementName; self.currentElementPointer.attributes = attributeDict; NSLog(@"name:%@" , elementName); } // 元素结束 - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName { self.currentElementPointer = self.currentElementPointer.parent; NSLog(@"end name:%@" , elementName); } // 解析文本,会多次解析,每次只解析1000个字符,如果多月1000个就会多次进入这个方法 - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string { if([self.currentElementPointer.text length] > 0){ self.currentElementPointer.text = [self.currentElementPointer.text stringByAppendingString:string]; }else{ self.currentElementPointer.text = [NSMutableString stringWithString:string]; } NSLog(@"string:%@" , string); } @end
libxml2
上面的代码十分冗长,因为不能用parser对象直接获得文档结构。所以我们采用libxml2,http://xmlsoft.org/,它是一套默认包含在iOS SDK中的开源类库,它是基于C语言的API,所以使用起来可能不如NSXML方便。这套类库同时支持DOM和SAX解析,libxml2的SAX解析方式还是非常酷的,因为它可以边读取边解析,尤其是在从网上下载一个很大的XML文件,就可以一边下载一边对已经下载好的内容进行解析,极大的提高解析效率。
但是基于C语言的api在代码中显得很不协调。
GDataXML类库
如果需要经常解析XML,第三方类库能带来更多方便。
GDataXMLDocument读取xml文件,并且解析成为dom对象:
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"xml"]; NSData *xmlData = [[NSMutableData alloc] initWithContentsOfFile:filePath]; NSError *error; GDataXMLDocument *doc = [[GDataXMLDocument alloc] initWithData:xmlData options:0 error:&error]; if (doc == nil) { return nil; } NSLog(@"%@", doc.rootElement); //[doc release]; //[xmlData release];