關於XML解析的blog有很多,我本來不想寫的;不過我發現有一些細節他們都沒有說,我這裡就多說一些細節。 我們在哪些地方用XML:現在json用的這麼多,使用XML通訊的已經不多了。我遇到的場景是,我們的伺服器有很多個,需要用戶去選擇。那麼我們就需要定期維護一個伺服器列表,這個伺服器列表的配置文件我
關於XML解析的blog有很多,我本來不想寫的;不過我發現有一些細節他們都沒有說,我這裡就多說一些細節。
我們在哪些地方用XML:現在json用的這麼多,使用XML通訊的已經不多了。我遇到的場景是,我們的伺服器有很多個,需要用戶去選擇。那麼我們就需要定期維護一個伺服器列表,這個伺服器列表的配置文件我需要每次下載。自然我就需要解析這一個文件。
XML解析有兩種模式SAX和DOM。我用的是系統的 NSXMLParser 它是SAX解析。
我寫了一個工具類,先貼一下代碼,下麵會有一些說明
.h
1 #import <Foundation/Foundation.h> 2 3 typedef NS_ENUM(NSInteger,xmlModelName){// 這裡我對我的XML文件做了區別 因為要解析表情和伺服器列表兩種 4 xmlModelNameFace, 5 xmlModelNameServer, 6 }; 7 @interface XMLParser : NSObject<NSXMLParserDelegate> 8 // 這個是回調的Block 9 @property(nonatomic,copy)void (^returnParseArray)(NSArray * returnArray); 10 @property(nonatomic,readonly)xmlModelName currentModelName; 11 12 - (instancetype)initWithFilePath:(NSString *)path fileType:(NSString *)fileType modelName:(xmlModelName)modelName; 13 14 - (void)startWithFilePath:(NSString *)path fileType:(NSString *)fileType; 15 @end
.m
1 #import "XMLParser.h" 2 #import "FaceModel.h" 3 #import "ToolClient.h" 4 #import "ServerModel.h" 5 @implementation XMLParser{ 6 NSMutableArray * faceArray; 7 NSMutableArray * serverArray; 8 } 9 10 @synthesize currentModelName; 11 - (instancetype)initWithFilePath:(NSString *)path fileType:(NSString *)fileType modelName:(xmlModelName)modelName{ 12 self = [super init]; 13 if(self){ 14 currentModelName = modelName; 15 } 16 return self; 17 } 18 19 - (void)startWithFilePath:(NSString *)path fileType:(NSString *)fileType { 20 [self parseWithPath:path type:fileType]; 21 } 22 // 23 - (void)parseWithPath:(NSString *)filePath type:(NSString *)fileType{ 24 if (currentModelName == xmlModelNameFace) { 25 faceArray = [[NSMutableArray alloc]init]; 26 }else if(currentModelName == xmlModelNameServer){ 27 serverArray = [[NSMutableArray alloc]init]; 28 } 29 NSData *xmlData = [[NSData alloc] initWithContentsOfFile:filePath]; 30 if(xmlData && xmlData.length > 10){ 31 NSXMLParser *parser = [[NSXMLParser alloc] initWithData:xmlData]; 32 [parser setShouldProcessNamespaces:NO]; 33 [parser setShouldReportNamespacePrefixes:NO]; 34 [parser setShouldResolveExternalEntities:NO]; 35 [parser setDelegate:self]; 36 BOOL success = [parser parse]; 37 if(success) { 38 [self parseSuccess]; 39 }else { 40 [ToolClient activityShowMessage:@"XML解析失敗" inView:[UIApplication sharedApplication].windows[0]]; 41 } 42 }else { 43 [ToolClient activityShowMessage:@"XML解析失敗" inView:[UIApplication sharedApplication].windows[0]]; 44 } 45 } 46 // 成功後的回調 47 - (void)parseSuccess { 48 if(self.returnParseArray){ 49 if (currentModelName == xmlModelNameFace) { 50 self.returnParseArray(faceArray); 51 }else if(currentModelName == xmlModelNameServer){ 52 self.returnParseArray(serverArray); 53 } 54 } 55 } 56 #pragma mark - NSXMLParserDelegate 57 58 - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName 59 namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName 60 attributes:(NSDictionary *)attributeDict { 61 // NSLog(@"Name:%@",elementName); 62 63 if([elementName isEqualToString:@"face"] && currentModelName == xmlModelNameFace) { 64 FaceModel * faceModel = [[FaceModel alloc]init]; 65 faceModel.kID = [attributeDict[@"id"]intValue]; 66 faceModel.kName = attributeDict[@"name"]; 67 faceModel.kImage = attributeDict[@"file"]; 68 [faceArray addObject:faceModel]; 69 faceModel = nil; 70 }else if([elementName isEqualToString:@"Server"] && currentModelName == xmlModelNameServer){ 71 ServerModel * sModel = [[ServerModel alloc]init]; 72 sModel.serverName = attributeDict[@"name"]; 73 sModel.serverIP = attributeDict[@"ChatServerIP"]; 74 sModel.chatPort = attributeDict[@"chatPort"]; 75 sModel.fileServerIP = attributeDict[@"fileServerIP"]; 76 sModel.filePort = attributeDict[@"filePort"]; 77 [serverArray addObject:sModel]; 78 sModel = nil; 79 } 80 } 81 - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string { 82 // NSLog(@"value:%@",string); 83 } 84 - (void)parserDidEndDocument:(NSXMLParser *)parser { 85 //86 // NSLog(@"%@",faceArray); 87 88 } 89 - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{ 90 // NSLog(@"elementName:%@",elementName); 91 // NSLog(@"qualifiedName:%@",qName); 92 // 93 // NSLog(@"NSXMLParserDone"); 94 // NSLog(@"%@",faceArray); 95 // NSLog(@"%i",(int)faceArray.count); 96 97 } 98 @end
現在我來說一下XML解析的設置裡面那3個設置為NO的參數是什麼作用
1 [parser setShouldProcessNamespaces:NO]; 2 [parser setShouldReportNamespacePrefixes:NO]; 3 [parser setShouldResolveExternalEntities:NO];
第一個 setShouldProcessNamespaces 這個屬性設置為YES的話,這兩個方法會有值輸出:parser:didStartElement:namespaceURI:qualifiedName:attributes:
和 parser:didEndElement:namespaceURI:qualifiedName: 這兩個在解析過程中都是都是可以看到裡面的節點或欄位的名字的。我覺得調試的時候可以用一下。
第二個 setShouldReportNamespacePrefixes 這個屬性設置為YES的話,這兩個方法會有值輸出:
parser:didStartMappingPrefix:toURI:
和 parser:didEndMappingPrefix: 這個我覺得完全沒有必要用它
第三個 setShouldResolveExternalEntities 這個屬性設置為YES的話,這個方法會有值輸出:parser:foundExternalEntityDeclarationWithName:publicID:systemID: 其中publicID 和systemID 都是XML文檔的特有的標識。官方文檔對這個兩個的變數都是這麼說的:
You may access this property once a parsing operation has begun or after an error occurs.
也就是當XML解析已經開始或者出現錯誤的時候,再去看它。也就是說如果你的XML寫的夠好 你就忽略它吧。
上面的代碼是工具類,下麵這段會告訴你這段代碼怎麼用:
1 // 解析文件 2 - (void)parseFile:(NSString *)filepath{ 3 NSLog(@"filepath%@",filepath);// 文件的路徑 4 if(data.length>10){// 簡單的長度檢測 5 __weak SelectServerViewController * ws = self;// 弱引用 6 // do parse 7 XMLParser * xp = [[XMLParser alloc]initWithFilePath:filepath fileType:@"xml" modelName:xmlModelNameServer]; 8 // 先設置回調 9 xp.returnParseArray = ^(NSArray * array){ 10 // 回調的結果 去給tableView 展示 11 [ws gotDataArray:array]; 12 }; 13 // 再開始解析 14 [xp startWithFilePath:filepath fileType:@"xml"]; 15 } 16 17 } 18 19 - (void)gotDataArray:(NSArray *)array { 20 if(array){ 21 // NSLog(@"array:%@",array); 22 dataArray = [array mutableCopy]; 23 [myTableView reloadData]; 24 } 25 }
XML解析我就寫這麼多了,給一個建議 解析的時候用一個Model來存儲數據,後續的使用會很方便。