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文件来读取数据

<?xml version="1.0" encoding="UTF-8"?>
<Books>
    <Book id="1">
        <title>title1</title>
        <author>author1</author>
        <summary>summary1</summary>
    </Book>
    
    <Book id="2">
        <title>title2</title>
        <author>author2</author>
        <summary>summary2</summary>
    </Book>
    
    <Book id="3">
        <title>title3</title>
        <author>author3</author>
        <summary>summary3</summary>
    </Book>
</Books>

</br>

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)

<?xml version="1.0" encoding="UTF-8"?>
(此处调用方法2)<Books>  

   (此处调用方法2)<Book id="1"> 
     (此处调用方法2)<title>(此处调用方法3)title1(此处调用方法4)</title>
       (此处调用方法2)<author>(此处调用方法3)author1(此处调用方法4)</author>
       (此处调用方法2)<summary>(此处调用方法3)summary1(此处调用方法4)</summary>
  (此处调用方法4) </Book>
    
  (下同)
    <Book id="2">
        <title>title2</title>
        <author>author2</author>
        <summary>summary2</summary>
    </Book>
    
  (下同)
    <Book id="3">
        <title>title3</title>
        <author>author3</author>
        <summary>summary3</summary>
    </Book>
</Books>

(此处调用方法5)

</br>

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

//KSXMLParser.h
@interface KSXMLParser : NSObject <NSXMLParserDelegate>

@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<NSString *,NSString *> *)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;
}

</br>

5、TBXML解析

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

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

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


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

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

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

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


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


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


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


6、TBXML解析的分析:

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


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

7、TBXML解析

//KSXMLParser.h
#import <Foundation/Foundation.h>
#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

</br>

二、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"}] }

</br>

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:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,607评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,239评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,960评论 0 355
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,750评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,764评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,604评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,347评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,253评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,702评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,893评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,015评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,734评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,352评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,934评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,052评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,216评论 3 371
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,969评论 2 355

推荐阅读更多精彩内容

  • 1 数据解析 在iOS开发中,常见的数据传输格式有两种:JSON和XML。目前JSON由于其体积小、传输速度快等优...
    岁与禾阅读 1,048评论 2 11
  • 网络传输数据时最常用的格式有两种:XML和JSON XML解析: 对于XML的解析,常用的方法有:Pull解析方式...
    飞行员suke阅读 1,608评论 0 1
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,657评论 18 139
  • 解析的基本概念 解析: 从事先规定好的格式中提取数据 解析的前提: 提前约定好格式,数据提供方按照格式提供数据,数...
    云之君兮鹏阅读 1,848评论 4 28
  • JSON JSON和XML都是需要解析的 JSON是一种轻量级的数据格式,一般用于数据交互服务器返回给客户端的数据...
    JonesCxy阅读 1,857评论 2 10