一、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"
(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解析方便點