iOS-進階整理04 - JSON與xml文件解析

一、JSON與XML的優缺點

XML與JSON共同點:

1.格式統一,符合標準
2.容易與其他系統繼續遠程交互,數據共享比較方便
缺點:
1.XML文件格式文件龐大,格式賦值,傳輸占用帶寬
2.服務器端和客戶端都需要花費大量的代碼來解析XML,不論服務器還是客戶端都使代碼變得異常復雜不易維護
3.客戶端不同瀏覽器直接解析XML的方式不一致,需要重復編寫很多代碼
3.服務器端和客戶端解析XML花費資源和數據

JSON
優點:

1.數據格式比較簡單,易于讀寫,格式都是壓縮的,占用帶寬小
2.易于解析這種語言
3.支持多種語言,包括ACtionScript,C,C#,java,PHP,Python等,方便服務器端解析
4.以為JSON格式能夠直接為服務器端代碼使用,大大簡化了服務器端的代碼量,且易于維護

缺點:

1.沒有XML格式推廣的深入人心和使用廣泛
2.JSON在Web Service中推廣還屬于初級階段。

二、JSON解析

JSON(JavaScript Object Notation)是一種輕量級的數據交換格式,才有完全獨立于語言的文本格式,易于閱讀和編寫,與易于解析和生成。
JSON文件有兩種結構:
對象:“名稱/值”對的集合,可以理解為字典
數組:值的有序列表

NSJSONSerializationi里面包含了兩種方法來通過不同的數據形式解析JSON數據

 //解析  
 NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];  
     
NSDictionary *dic2 = [NSJSONSerialization writeJSONObject:<#(nonnull id)#>   
                     toStream:<#(nonnull NSOutputStream *)#> options:<#(NSJSONWritingOptions)#>   
                     error:<#(NSError * _Nullable __autoreleasing * _Nullable)#>]  
//通用的json解析方法  
//有時json文件前面會有一些不屬于json的字符,人為去除  
  
-(void)normalJsonData  
{  
    //得到路徑  
    NSString *path = [[NSBundle mainBundle]pathForResource:@"MovieList" ofType:@"txt"];  
    //由于不確定是否為標準格式的json串,先用字符串來接收文件數據  
    NSString *fileStr = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];  
    //判斷字符串是否存在并且是以 { 或者 [ 開始的,,是則說明文件為標準json串,否則需要截取字符  
    if (!(fileStr && ([fileStr hasPrefix:@"{"] || [fileStr hasPrefix:@"["])) )  
    {  
        //聲明不是標準格式json,找到第一個{ 和 [ 的位置  
        NSRange range = [fileStr rangeOfString:@"{"];  
        NSRange range_1 = [fileStr rangeOfString:@"["];  
          
        NSLog(@"%@,%@",NSStringFromRange(range),NSStringFromRange(range_1));  
          
        if (range.location > range_1.location)  
        {  
            // 中括號 [ 開頭從中括號位置開始截取  
            fileStr = [fileStr substringFromIndex:range_1.location];  
        }  
        else  
        {   // 花括號{ 開頭  
            fileStr = [fileStr substringFromIndex:range.location];  
        }  
    }  
    //經過處理fileStr為標準的json串  
    NSData *resultData = [fileStr dataUsingEncoding:NSUTF8StringEncoding];  
    //解析  
    id resultValue = [NSJSONSerialization JSONObjectWithData:resultData options:NSJSONReadingAllowFragments error:nil];  
      
    NSLog(@"id = %@",resultValue);  
}  

三、XML解析

XML有兩種解析方法:DOM(Document Object Model)解析和SAX(Simple API for XML)工具

1.DOM解析XML,使用GDataXMLNode文件

DOM解析時,讀入整個XML文檔并構建一個駐留內存的樹結構(節點樹),通過遍歷樹結構可以檢索任意XML節點,讀取屬性和值。
通常可以借助XPath,直接查詢XML節點
iOS包含一個C語言動態鏈接庫libxml2.tbd(xcode7以前為libxml2.dylib)
GDataXMLNode是Google提供的開元XML解析類,對libxml2.tbd進行了Objective-C的封裝,能對較小或中等的xml穩定進行讀寫操作并指出XPath語法。

(1)拖入GDataXMLNode的.h和.m文件到工程
這時候進行編譯,會報錯"libxml/tree/h file not found"

(2)在工程的Build phases設置的Link Binary With Libraries里面點擊加號,搜索libxml2.tbd,選擇添加

(3)在工程的Build Settings設置里面搜索search,找到Search paths選項下的Header Search Paths,加入一條/usr/include/libxml2這里三個反斜杠不能少



這時候進行編譯,可能會出現20個左右的錯誤,因為GDataXMLNode.m文件使用的是MRC模式,如果我們的工程是ARC模式下的,是不能使用autorelease等內存操作的。

(4)再進入Build phases設置的Compile Sources選項中,選擇GDataXMLNode.m文件,點擊文件后面的空白,添加一句-fno-objc-arc。



這時候再編譯,終于沒啥問題了,可以開始用了,別嫌麻煩,sax不用配置但用起來麻煩
要解析的XML文件為下面的內容文件名為XMLDemo.xml


<?xml version="1.0" encoding="utf-8"?> <!--此行包含XML的版本信息和編碼格式-->  
<students><!--這是開始標簽,也就是根節點-->  
    <student attribute = "吉祥物"><!--student為根節點的子節點,name節點的父節點, attribute是它的屬性-->  
        <name>李帥</name><!--洛洛受為name節點的值-->  
        <sex>無</sex>  
        <age>14</age>  
    </student>  
    <student attribute = "吉祥物之基友">  
        <name>建華</name>  
        <sex>隨條件改變</sex>  
        <age>17</age>  
    </student>  
</students><!--節點的結束標簽都是以/加標簽名稱組成 -->  
//dom解析  
-(void)domParser  
{  
    //獲得文件路徑  
    NSString *path = [[NSBundle mainBundle]pathForResource:@"XMLDemo" ofType:@"xml"];  
    //用NSData接收  
    NSData *xmlData = [NSData dataWithContentsOfFile:path];  
    //將文件類型通過note對象轉換為樹形結構(一次性從內存中將XML文件轉換為倒著的樹形結構)  
    GDataXMLDocument *doc = [[GDataXMLDocument alloc]initWithData:xmlData options:0 error:nil];  
    //得到根節點  
    GDataXMLElement *rootElement = [doc rootElement];  
  
   //得到根節點的所有節點,這個方法很重要,通過這個方法一層層得到子節點  
    NSArray *stuElement = [rootElement elementsForName:@"student"];  
      
    //初始化一個可變數組,用來存放每個學生的信息  
    NSMutableArray *allStudentMArray = [[NSMutableArray alloc]init];              
    //得到student節點的所有子節點  
    for (GDataXMLElement* itemElement in stuElement) {  
        //itemElement是student節點,分別取出student的子節點  
        NSArray *nameElement = [itemElement elementsForName:@"name"];  
        NSString *name = [[nameElement objectAtIndex:0]stringValue];  
        NSArray *sexElement = [itemElement elementsForName:@"sex"];  
        NSString *sex = [[sexElement objectAtIndex:0]stringValue];  
        NSArray *ageElement = [itemElement elementsForName:@"age"];  
        NSString *age = [[ageElement objectAtIndex:0]stringValue];  
          
        NSDictionary *dic = [[NSDictionary alloc]initWithObjectsAndKeys:name,@"name",sex,@"sex",age,@"age", nil nil];  
        [allStudentMArray addObject:dic];  
  
        //得到節點的屬性  
        NSArray *attributes = itemElement.attributes;  
        //得到屬性節點  
        GDataXMLNode *node = [itemElement attributeForName:@"attribute"];  
          
        NSLog(@"attr =  %@ ,  node.name = %@ node.stringValue = %@  ",attributes[0],node.name,node.stringValue);  
    }  
    NSLog(@"%@",allStudentMArray);  
}  
//通過dom解析的方式為xml增加節點(sax解析只可以讀取,不可以添加)  
-(void)domAddNote  
{  
    //獲得文件路徑  
    NSString *path = [[NSBundle mainBundle]pathForResource:@"XMLDemo" ofType:@"xml"];  
    //用NSData接收  
    NSData *xmlData = [NSData dataWithContentsOfFile:path];  
    //得到  
    GDataXMLDocument *doc = [[GDataXMLDocument alloc]initWithData:xmlData options:0 error:nil];  
    //得到根節點  
    GDataXMLElement *rootElement = [doc rootElement];  
    //創建一個我們需要添加的節點(student)  
    GDataXMLElement *createElement = [GDataXMLNode elementWithName:@"student"];  
    GDataXMLElement *nameNode = [GDataXMLElement elementWithName:@"name" stringValue:@"成功"];  
    [createElement addChild:nameNode];  
    GDataXMLElement *sexNode = [GDataXMLElement elementWithName:@"sex" stringValue:@"男"];  
    [createElement addChild:sexNode];  
    GDataXMLElement *ageNode = [GDataXMLElement elementWithName:@"age" stringValue:@"11"];  
    [createElement addChild:ageNode];  
    //將創建好的student節點添加到根節點  
    [rootElement addChild:createElement];  
//    [rootElement removeChild:createElement];//刪除  
            
    //得到所有的student節點  
    NSArray *stuElementArray = [rootElement elementsForName:@"student"];  
    //遍歷根節點  
      
    for (GDataXMLElement *stuItem in stuElementArray) {  
        //name  
        NSString *name = [[[stuItem elementsForName:@"name"]objectAtIndex:0]stringValue];  
        NSString *sex = [[[stuItem elementsForName:@"sex"]objectAtIndex:0]stringValue];  
        NSString *age = [[[stuItem elementsForName:@"age"]objectAtIndex:0]stringValue];  
        NSLog(@"name = %@,sex = %@,age = %@",name,sex,age);  
    }  
}  
2.SAX解析

SAX是基于事件驅動的解析方式,逐行進行事件解析(采用協議回調機制)
解析過程:開始標簽->取值->結束標簽->取值

//xml的sax解析,逐行解析  
-(void)saxParser  
{  
    NSString *path = [[NSBundle mainBundle]pathForResource:@"XMLDemo" ofType:@"xml"];  
    NSData *data = [NSData dataWithContentsOfFile:path];  
    //sax解析  
    NSXMLParser *parser = [[NSXMLParser alloc]initWithData:data];  
    //指定代理  
    parser.delegate = self;  
    //開始解析(同步,解析未完成,下面的代碼就不會執行  
    BOOL isSuccess = [parser parse];  
      
    if (isSuccess) {  
        NSLog(@"sax ok");  
    }  
    else  
    {  
        NSLog(@"sax fs");  
    }  
}  

SAX解析的代理方法

//導入協議NSXMLParserDelegate  
@interface RootViewController ()<NSXMLParserDelegate>  
  
#pragma mark -- sax解析的代理方法  
  
//開始解析  
-(void)parserDidStartDocument:(NSXMLParser *)parser  
{  
    NSLog(@"開始解析");  
    self.allStudentsMArray = [[NSMutableArray alloc]init];  
}  
//解析結束  
-(void)parserDidEndDocument:(NSXMLParser *)parser  
{  
    NSLog(@"解析結束");  
    NSLog(@"array -- %@",self.allStudentsMArray);  
}  
//開始解析節點  
//elementName:標簽名稱  
//namespaceURI;命名空間指向的鏈接  
//qName:命名空間代名詞  
//attributeDict:節點的屬性值  
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary<NSString *,NSString *> *)attributeDict  
{  
    NSLog(@"開始解析節點--%@",elementName);  
    if ([elementName isEqualToString:@"student"]) {  
        //當解析到student的時候,說明已經到了該獲取該節點子節點的值的時候,應該初始化容器了  
        self.singleStudentDic = [NSMutableDictionary dictionary];  
    }  
}  
  
//string為所需要的值的一部分  
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string  
{  
    if (self.noteValueMstring) {  
  
 //對解析數據進行拼接  
        [self.noteValueMstring appendString:string];  
        NSLog(@"%@",_noteValueMstring);  
    }  
    else  
    {  
        self.noteValueMstring = [NSMutableString stringWithString:string];  
    }  
}  
  
//節點結束  
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{  
    if ([elementName isEqualToString:@"name"]) {  
        //說明name取值結束  
        [_singleStudentDic setObject:[self replaceStringWith:_noteValueMstring] forKey:elementName];  
    }  
    
    if ([elementName isEqualToString:@"age"]) {  
        //說明age節點取值完成  
        [_singleStudentDic setObject:[self replaceStringWith:_noteValueMstring] forKey:elementName];  
    }  
    if ([elementName isEqualToString:@"sex"]) {  
        [_singleStudentDic setObject:[self replaceStringWith:_noteValueMstring] forKey:elementName];  
    }  
    if ([elementName isEqualToString:@"student"]) {  
        //說明student的解析結束了,該把字典放入數組了  
        [self.allStudentsMArray addObject:_singleStudentDic];  
//        [_singleStudentDic removeAllObjects];在開始解析節點的時候對dic進行了初始化  
    }  
    //每次解析完成一個節點,都需要將可變字符串清理一次  
    _noteValueMstring = nil;  
}  
  
//替換特殊字符,SAX解析會把XML中的各種換行和tab空格等字符解析出來,人為修改一下  
-(NSString*)replaceStringWith:(NSString*)sourceStr  
{  
    NSString *resultStr = [sourceStr stringByReplacingOccurrencesOfString:@"\r" withString:@""];  
    resultStr = [resultStr stringByReplacingOccurrencesOfString:@"\n" withString:@""];  
    resultStr = [resultStr stringByReplacingOccurrencesOfString:@"\t" withString:@""];  
    resultStr = [resultStr stringByReplacingOccurrencesOfString:@" " withString:@""];  
      
    return resultStr;  
}  

這么看來還是DOM解析方便點

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容