XML和JSON数据解析

XML和JSON是两种数据交换格式。

  • XML是老牌、经典、灵活的数据交换格式
  • JSON是比XML轻便的数据交换格式,广泛用于Web和移动平台开发

一、XML数据解析

1、解析XML文档的两种模式

(1) SAX:事件驱动模式。从根元素开始,按顺序一个个元素往下解析,遇到开始标签、结束标签和属性等就会触发相应事件。它只在XML文档中查找特定条件的内容,并且只提取需要的东西,占用内存少,比较灵活。

优点:占用内存少,解析速度快,适合解析大文件
缺点:只能读取XML文档,不能写入或修改XML文档

(2)DOM:文档对象模型。一次性将整个XML文档加载进内存,将XML文档作为一棵树状结构进行分析,需要的时候查找特定的结点。

优点:实现简单,读写平衡(可读可写),适合解析小文件
缺点:占用内存较多,解析速度较慢,不适合解析大文件

2、常用XML解析

(1)NSXML(iOS SDK自带,苹果默认解析框架,采用SAX模式)
(2)TBXML(第三方开源库,采用DOM模式)

以下例子将使用下面的Books.xml文件来读取数据



    
        title1
        author1
        summary1
    
    
    
        title2
        author2
        summary2
    
    
    
        title3
        author3
        summary3
    


3、NSXML解析

NSXML框架的核心是NSXMLParser及其委托协议NSXMLParserDelegate,主要的解析工作在NSXMLParserDelegate实现类中完成。

其中NSXMLParserDelegate有5个常用的方法:

(1)在文档开始的时候触发

parserDidStartDocument:

(2)遇到一个开始标签时触发,其中namespaceURI部分是命名空间,qualifiedName是限定名,attributes是字典类型的属性集合

parser:didStartElement:namespaceURI:qualifiedName:attributes:

(3)遇到字符串时触发

parser:foundCharacters:

(4)遇到结束标签时触发

parser:didEndElement:namespaceURI:qualifiedName:

(5)在文档结束时触发

parserDidEndDocument:

4、NSXML解析的分析:结合Books.xml文件来说明每个方法的调用时机

(此处调用方法1)


(此处调用方法2)  

   (此处调用方法2) 
     (此处调用方法2)(此处调用方法3)title1(此处调用方法4)
       (此处调用方法2)(此处调用方法3)author1(此处调用方法4)
       (此处调用方法2)(此处调用方法3)summary1(此处调用方法4)
  (此处调用方法4) 
    
  (下同)
    
        title2
        author2
        summary2
    
    
  (下同)
    
        title3
        author3
        summary3
    


(此处调用方法5)


所以我们可以自定义一个类,如KSXMLParser,并让其实现NSXMLParserDelegate协议

//KSXMLParser.h
@interface KSXMLParser : NSObject 

@property (strong, nonatomic) NSMutableArray *list; //把读取的每一个Book数据作为一个数组元素保存

@property (strong, nonatomic) NSString *currentElementName; //记录当前读取的Element,如"Book","title","author"等

- (void)startParser;

@end
#import "KSXMLParser.h"

@implementation KSXMLParser

- (void)startParser {
    NSString *path = [[NSBundle mainBundle] pathForResource:@"Books" ofType:@"xml"];
    
    NSURL *url = [NSURL fileURLWithPath:path];
    
    NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:url];
    parser.delegate = self;
    
    [parser parse];
}

//方法1
- (void)parserDidStartDocument:(NSXMLParser *)parser {
    self.list = [[NSMutableArray alloc] init];
}

//方法2
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
    
    self.currentElementName = elementName;
    
    if ([self.currentElementName isEqualToString:@"Book"]) {
        
        NSString *identifier = [attributeDict objectForKey:@"id"];
        NSMutableString *book = [[NSMutableString alloc] initWithString:identifier];
        
        [self.list addObject: book];
    }
}

//方法3
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
    
    string = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
    if ([string isEqualToString:@""]) {
        return;
    }
    
    if ([self.currentElementName isEqualToString:@"title"] && [self.list count]) {
        
        NSMutableString * book = (NSMutableString *)[self.list lastObject];
        
        [book appendFormat:@" %@", string];
        
    }
    
    if ([self.currentElementName isEqualToString:@"author"] && [self.list count]) {

        NSMutableString * book = (NSMutableString *)[self.list lastObject];
        
        [book appendFormat:@" %@", string];
    }
    
    if ([self.currentElementName isEqualToString:@"summary"] && [self.list count]) {
        
        NSMutableString *book = (NSMutableString *)[self.list lastObject];
        
        [book appendFormat:@" %@", string];
    }
}

//方法4
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI {
    
}

//方法5
- (void)parserDidEndDocument:(NSXMLParser *)parser {
    
}

@end
//ViewController.m

#import "ViewController.h"
#import "KSXMLParser.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    KSXMLParser *parser = [[KSXMLParser alloc] init];
    [parser startParser];
    
    for (NSString *str in parser.list) {
        NSLog(@"%@", str);
    }
}

@end

输出

NSXMLParser[1326:111640] 1 title1 author1 summary1
NSXMLParser[1326:111640] 2 title2 author2 summary2
NSXMLParser[1326:111640] 3 title3 author3 summary3

补充说明:
方法2中的下面这2条语句是用来消除XML文本中遇到的回车符和空格的。仅仅方法2需要这2条语句,是因为方法2在遇到换行符和回车符等特殊字符也会触发,所以需要做处理。

string = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];

if ([string isEqualToString:@""]) {
        return;
}


5、TBXML解析

因为TBXML是第三方开源库,所以不像iOS自带的那么方便,使用时需要下载,然后配置环境。

我们可以选择手动配置或使用CocoaPods帮我们配置,这里我将详细演示前者的配置方法。

(1)下载完成并解压后,将TBXML-Headers和TBXML-Code文件夹添加到工程中,如下图


XML和JSON数据解析_第1张图片

(2)在工程中添加TBXML所依赖的Framework和库,有2个:libz.tbd和CoreGraphics.framework,步骤:TARGETS-->Build Phases-->Link Binary With Libraries,点击“+”按钮搜索并添加

XML和JSON数据解析_第2张图片
XML和JSON数据解析_第3张图片

(3)此外,还需要有预编译文件,包括两个步骤,创建和配置

通过在Xcode菜单点击File-->New-->File或快捷键Command+N选择PCH File文件模板创建预编译头文件

XML和JSON数据解析_第4张图片

在预编译头文件中添加选中的那2行代码,因为TBXML默认支持MRC,是ARC_ENABLED宏可以打开ARC开关,使TBXML能够支持ARC工程。


XML和JSON数据解析_第5张图片

配置预编译头文件到工程中的步骤是TARGETS-->Build Settings-->Apple LLVM 8.0 - Language --> Prefix Header中输入预编译头文件的文件名


XML和JSON数据解析_第6张图片

(4)完成以上工作之后执行一次编译,如果出现以下错误:


XML和JSON数据解析_第7张图片

说明在创建预编译头文件时,没有把它放到与.xcodeproj同一个目录里,把它放到那里问题就可以解决了,如下图:


XML和JSON数据解析_第8张图片

6、TBXML解析的分析:

如前文第1点和第2点所述,TBXML是采用DOM模式解析的,它会将XML文件转换为一棵树进行分析,当读取Books.xml文件时,会按如下生成的树结构进行解析


XML和JSON数据解析_第9张图片

解析顺序:title1,author1,summary1 --> title2,author2,summary2 --> title3,author3,summary3

7、TBXML解析

//KSXMLParser.h
#import 
#import "TBXML.h"

@interface KSXMLParser : NSObject

@property (strong, nonatomic) NSMutableArray *list;

- (void)startParse;

@end
//KSXMLParser.m
#import "KSXMLParser.h"

@implementation KSXMLParser

- (void)startParse {
    
    self.list = [[NSMutableArray alloc] init];
    
    TBXML *tbxml = [[TBXML alloc] initWithXMLFile:@"Books.xml" error: nil];
    
    TBXMLElement *root = tbxml.rootXMLElement;
    
    //如果root元素有效
    if (root) {
        TBXMLElement *bookElement = [TBXML childElementNamed:@"Book" parentElement:root];
        
        while (bookElement != nil) {
            NSMutableString *str = [[NSMutableString alloc] init];
            
            //获取Book id
            NSString *s = [TBXML valueOfAttributeNamed:@"id" forElement:bookElement error:nil];
            [str appendFormat:@" %@", s];
            
            //获取title
            TBXMLElement *titleElement = [TBXML childElementNamed:@"title" parentElement:bookElement];
            if (titleElement != nil) {
                NSString *s = [TBXML textForElement: titleElement];
                [str appendFormat:@" %@", s];
            }
            
            //获取author
            TBXMLElement *authorElement = [TBXML childElementNamed:@"author" parentElement:bookElement];
            if (authorElement != nil) {
                NSString *s = [TBXML textForElement:titleElement];
                [str appendFormat:@" %@", s];
            }
            
            //获取summary
            TBXMLElement *summaryElement = [TBXML childElementNamed:@"summary" parentElement:bookElement];
            if (summaryElement != nil) {
                NSString *s = [TBXML textForElement:summaryElement];
                [str appendFormat:@" %@", s];
            }
            
            [self.list addObject:str];
            
            bookElement = [TBXML nextSiblingNamed:@"Book" searchFromElement:bookElement];
            
        }
    }
}

@end
//ViewController.m
#import "ViewController.h"
#import "KSXMLParser.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    KSXMLParser *parser = [[KSXMLParser alloc] init];
    [parser startParse];
    
    for (NSString *str in parser.list) {
        NSLog(@"%@", str);
    }
}

@end

输出

TBXML[933:67915] 1 title1 title1 summary1
TBXML[933:67915] 2 title2 title2 summary2
TBXML[933:67915] 3 title3 title3 summary3


二、JSON数据解析

1、构成JSON文档的两种结构

  • 对象,里面的每个基本单位为“名称-值”,类似于Objective-C字典,是无序集合
  • 数组,里面的每个基本单位为“值”,“值”可以是双引号括起来的字符串、数值、true、false、null、对象或数组,类似于Objective-C数组,是有序集合

对象结构的语法

{
    " " : " ",
    " " : " ",
    " " : " ",
    ......
}

{
    "name" : "a.htm",
    "size" : 345,
    "saved" : true
}

数组结构的语法

[ " ", " ", " ", ...... ]

["text", "html", "css"]

对象结构和数组结构可以嵌套,如我们将在下面JSON解析时用到的data.json文件

{"ResultCode" : 0 ,
"Record" : [{"ID":"1", "title":"title1", "author":"author1", "summary":"summary1"},
            {"ID":"2", "title":"title2", "author":"author2", "summary":"summary2"},
            {"ID":"3", "title":"title3", "author":"author3", "summary":"summary3"}] }


2、常用JSON解析

(1)NSJSONSerialization

3、NSJSONSerialization解析

因为是在iOS5后添加进SDK的框架,所以可以很方便使用,同时也有非常优秀的性能

//ViewController.m
#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSString *path = [[NSBundle mainBundle] pathForResource:@"data" ofType:@"json"];
    
    NSData *jsonData = [[NSData alloc] initWithContentsOfFile:path];
    
    NSError *error;
    id jsonObj = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error];
    if (!jsonObj || error) {
        NSLog(@"JSON解码失败");
    }
    
    NSArray *listData = jsonObj[@"Record"];
    for (NSString *str in listData) {
        NSLog(@"%@", str);
    }
}

@end

输出

NSJSONSerialization[955:73984] {
    ID = 1;
    author = author1;
    summary = summary1;
    title = title1;
}
NSJSONSerialization[955:73984] {
    ID = 2;
    author = author2;
    summary = summary2;
    title = title2;
}
NSJSONSerialization[955:73984] {
    ID = 3;
    author = author3;
    summary = summary3;
    title = title3;
}

补充说明:
NSJSONSerialization的静态方法JSONObjectWithData: options: error:,返回的是字典类型。

(以下出自《iOS开发指南》(第4版) p462)
其中options参数指定了解析JSON的模式。该参数是枚举类型NSJSONReadingOptions定义的,有如下3个常量。

  • MutableContainers。指定解析返回的是可变的数组或字典。如果以后需要修改结果,这个常量是合适的选择
  • MutableLeaves。指定叶节点是可变字符串
  • AllowFragments。指定顶级节点可以不是数组或字典

此外,NSJSONSerialization还提供了JSON编码的方法, dataWithJSONObject: options: error:writeJSONObject: toStream: options: error:

你可能感兴趣的:(XML和JSON数据解析)