前言
@interface NSXMLParser : NSObject
public class NSXMLParser : NSObject
1、XML 数据
- XML(Extensible Markup Language)是可扩展标记语言的缩写,其中的标记(markup)是关键部分。可以创建内容,然后使用限定标记标记它,从而使每个单词、短语或块成为可识别、可分类的信息。创建的文件或文档实例由元素(标记)和内容构成。当从打印输出读取或以电子形式处理文档时,元素能够帮助更好地理解文档。元素的描述性越强,文档各部分越容易识别。自从出现标记至今,带有标记的内容就有一个优势,即在计算机系统缺失时,仍然可以通过标记理解打印出来数据。标记语言从早期的私有公司和*制定形式逐渐演变成标准通用标记语言(Standard Generalized Markup Language,SGML)、超文本标记语言(Hypertext Markup Language,HTML),并且最终演变成 XML。SGML 比较复杂,HTML(实际上仅是一组元素集)在识别信息方面不够强大。XML 则是一种易于使用和易于扩展的标记语言。
1.1 构建 XML
XML 文件由内容和标记组成。通过以标记包围内容的方式将大部分内容包含在元素中。例如,假设需要创建一本烹饪书。需要用 XML 编写名为 Ice Cream Sundae 的食谱。为了标记食谱名,需要将这个文本包含到元素中,即分别在文本的首末两端添加开始和结束标记。可以将元素命名为 recipename。要标记元素的开始标记,像这样将元素名放到尖括号中(<>):
<recipename>
。然后输入文本 Ice Cream Sundae。在文本的后面输入结束标记,即将元素名放在尖括号内,然后在元素名前面加上一个终止斜杠(/),比如:</recipename>
。这些标记构成一个元素<recipename> Ice Cream Sundae </recipename>
,可以在元素的内部添加内容或其他元素。可以为某个文档或文档集创建元素名。可以创建规则让元素根据特定需求组合起来。元素名可以是比较具有针对性的,也可以是比较通用的。还可以创建决定添加何种元素的规则。这些规则可以是严格的,也可以是松散的。一定要为文档创建元素,以识别认为重要的部分。
1.2 创建文件声明
- XML 文档的第一行可以是一个 XML 声明。这是文件的可选部分,它将文件识别为 XML 文件,有助于工具和人类识别 XML(不会误认为是 SGML 或其他标记)。可以将这个声明简单地写成
<?xml?>
,或包含 XML 版本(),甚至包含字符编码,比如针对 Unicode 的<?xml version="1.0" encoding="utf-8"?>
。因为这个声明必须出现在文件的开头,所以如果打算将多个小的 XML 文件合并为一个大 XML 文件,则可以忽略这个可选信息。
1.3 创建根元素
根元素的开始和结束标记用于包围 XML 文档的内容。一个文件只能有一个根元素,并且需要使用 “包装器” 包含它。清单 1 显示了经过删节的示例,其中的根元素名为
<recipe>
。在构建文档时,内容和其他标记必须放在<recipe>
和</recipe>
之间。-
清单 1. 根元素
<?xml version="1.0" encoding="UTF-8"?>
<recipe>
</recipe>
1.4 命名元素
标记的大小写保持一致创建 XML 时,要确保开始和结束标记的大小写是一致的。如果大小写不一致,在使用或查看 XML 时将出现错误。例如,如果大小写不一致,Internet Explorer 将不能显示文件的内容,但它会显示开始和结束标记不一致的消息。
-
到目前为止,都使用
<recipe>
作为根元素。在 XML 中,先要为元素选择名称,然后再根据这些名称定义相应的 DTD 或 schema。创建名称时可以使用英文字母、数字和特殊字符,比如下划线(_)。下面给出命名时需要注意的地方:- 元素名中不能出现空格。
- 名称只能以英文字母开始,不能是数字或符号。(在第一个字母之后就可以使用字母、数字或规定的符号,或它们的混合)。
- 对大小写没有限制,但前后要保持一致,以免造成混乱。
继续以前面的示例为例,如果添加了名为
<recipename>
的元素,它将有一个开始标记<recipename>
和相应的结束标记</recipename>
。-
清单 2. 更多元素
<?xml version="1.0" encoding="UTF-8"?>
<recipe>
<recipename>Ice Cream Sundae</recipename>
<preptime>5 minutes</preptime>
</recipe> XML 文档可以使用内部不包含任何内容的空标记,这些标记可以表示为单个标记,而不是一组开始和结束标记。以类似于 HTML 的文件为例,里面的
<img src="mylogo.gif">
是一个独立的元素。它不包含任何子元素或文本,因此它是一个空元素,可以将它表示为<img src="mylogo.gif" />
(以一个空格和熟悉的终止斜杠结束)。
1.5 嵌套元素
嵌套即把某个元素放到其他元素的内部。这些新的元素称为子元素,包含它们的元素称为父元素。
<recipe>
根元素中嵌套有几个其他元素,如清单 3 所示。这些嵌套的子元素包括<recipename>、<ingredlist>
和<preptime>
。<ingredlist>
元素内部包含多个子元素<listitem>
。XML 文档可以使用多层嵌套。一个常见的语法错误是父元素和子元素的错误嵌套。任何子元素都要完全包含在其父元素的开始和结束标记内部。每个同胞(Sibling)元素必须在下一个同胞元素开始之前结束。清单 3 的代码显示了正确的嵌套。这些标记的开始和结束没有与其他标记混合在一起。
-
清单 3. 正确嵌套的 XML 元素
<?xml version="1.0" encoding="UTF-8"?>
<recipe>
<recipename>Ice Cream Sundae</recipename>
<ingredlist>
<listitem>
<quantity>3</quantity>
<itemdescription>chocolate syrup or chocolate fudge</itemdescription>
</listitem>
<listitem>
<quantity>1</quantity>
<itemdescription>nuts</itemdescription>
</listitem>
<listitem>
<quantity>1</quantity>
<itemdescription>cherry</itemdescription>
</listitem>
</ingredlist>
<preptime>5 minutes</preptime>
</recipe>
1.6 添加属性
有时候要为元素添加属性。属性由一个名称--值对构成,值包含在双引号中,比如:type="dessert"。属性是在使用元素时存储额外信息的一种方式。在同一个文档中,可以根据需要对每个元素的不同实例采用不同的属性值。
可以在元素的开始标记内部输入一个或多个属性,比如:
<recipe type="dessert">
。如果要添加多个属性,各个属性之间使用空格分开,比如:<recipename cuisine="american" servings="1">
。清单 4 显示了当前的 XML 文件。-
清单 4. 带有元素和属性的 XML 文件
<?xml version="1.0" encoding="UTF-8"?>
<recipe type="dessert">
<recipename cuisine="american" servings="1">Ice Cream Sundae</recipename>
<preptime>5 minutes</preptime>
</recipe> 可以根据需要使用任意数量的属性。要考虑需要添加到文档的细节。如果要对文档分类,属性尤其有用,比如按照菜谱的 type 进行分类。属性名可以包含在元素名中使用的字符,规则也是类似的,即字符之间不能带有空格,名称只能以字母开始。
1.7 构造良好并且有效的 XML
如果根据结构规则创建 XML,就很容易实现构造良好的 XML。构造良好的 XML 即遵循所有 XML 规则创建的 XML:正确的元素命名,嵌套,属性命名等等。
要实现构造良好的 XML 取决于如何处理 XML。但考虑一下前面提到的示例,它要求根据菜谱类型进行分类。您需要确保每个
<recipe>
元素都包含 type 属性,以对菜谱进行分类。能够正确验证并确保存在属性值是非常重要的(避免出现双关语)。验证就是根据元素规则检查文档的结构,以及如何为每个父元素定义子元素。这些规则是在 文档类型定义(Document Type Definition,DTD)或模式(schema )中定义的。验证要求您创建自己的 DTD 或 schema ,然后在 XML 文件中引用 DTD 或 schema 文件。
为了实现验证,必须在 XML 文档的顶部附近包含文档类型(DOCTYPE)。这行代码将引用用于验证文档的 DTD 或 schema(元素和规则列表)。例如,DOCTYPE 可能类似于 清单 5。
-
清单 5. DOCTYPE
<!DOCTYPE MyDocs SYSTEM "filename.dtd">
这个例子假设元素列表文件的名称是 filename.dtd,并且位于您的计算机上(如果指向公共文件位置,则 SYSTEM 和 PUBLIC 是相对的)。
1.8 使用实体
实体可以是文本短语或特殊字符。它们可以指向内部或外部。必须正确地声明和表示实体,以避免错误和确保正确显示。
不能直接在内容中输入特殊字符。如果要在文本中使用符号,必须使用它的字符代码将它设置为实体。您可以将短语(比如公司名)设置为实体,然后就可以在内容中使用该实体。为了设置实体,必须先为它创建一个名称,然后将它输入到内容中,以 and 符号(&)开始,并以分号(;)结束 — 例如,&coname;。然后在 DOCTYPE 的方括号([])内部输入代码,如 清单 6 所示。这个代码识别表示实体的文本。
-
清单 6. ENTITY
<!DOCTYPE MyDocs SYSTEM "filename.dtd" [<!ENTITY coname "Rabid Turtle Industries"]>
使用实体可以避免反复输入相同的短语和信息。在很多情况下它还使得调整文本更加容易(变更公司名时),只需对实体定义进行简单调整。
1.9 避免错误
在学习创建 XML 文件时,在 XML 编辑器中打开它,以检查它的结构是否良好,并且确保您遵循 XML 规则。例如,如果您使用 Windows® Internet Explorer®,就可以在浏览器中打开 XML。如果它能够显示 XML 元素、属性和内容,则表明 XML 是构造良好的。相反,如果显示错误,则很可能是出现语法错误,您需要小心检查文档,看看是不是丢失标记和标点符号或输入错误。
如在嵌套元素小节中提到的一样,包含其他元素的元素就是被包含元素的父元素。在下面的示例中,
<recipe>
是根元素,并且包含文件的完整内容。父元素<recipe>
包含的子元素有<recipename>、<ingredlist>、<directions>
等等。在这种结构中,<recipename>、<ingredlist>
和<directions>
成了同胞元素。此外,还要正确嵌套同胞 元素。
1.10 iOS XML 解析方式
-
1、DOM 解析:
- 是在 MAC 中使用的解析方式。DOM 方式的 XML 解析内存消耗非常大,iOS 默认不支持 DOM 方式解析。
- DOM 方式解析一次性将 XML 文档以树型结构读入内存,可以读,可以改,内存消耗极大,横向的节点越多,内存消耗越大。
- 使用 DOM 解析适合于小的 XML 解析,并且能够动态维护,不适用于手机,主要用在 PC 端或者服务器端。
- 在 MAC 中提供一个 NSXML 的类,可以实现 DOM 方式的解析。
- 有一些第三方框架是能够实现 DOM 方式的解析,使用 DOM 解析,适合非常小的 XML 数据结构。
-
2、SAX 解析:
- 是苹果提供的解析方式。
- 是只读的方式,从上向下的方式解析,解析速度快,适合大的 XML 文件解析。
- NSXMLParser 通过代理实现解析。解析的时候相对比较繁琐,有 5 个代理方法,每个代理方法都要写一定代码。
-
3、第三方框架 解析:
-
GDataXML-HTML 框架:
是 iOS 中 DOM 方式解析 XML 的第三方框架。在 iOS 开发中,如果要使用 DOM 方式解析,最好只处理小的 XML。
GDataXMLDocument 对应一个完整的 XML 文档,实例化之后,会把整个 XML 以树型结构读入内存。
解析可读、可写。 addChild 添加节点, removeChild 删除节点。
所有的 DOM 解析,里面都叫 name 和 stringValue 属性。
-
KissXML 框架:
- XMPP 中用到 DOM 方式解析 XML 的第三方框架。
-
2、系统方式 XML 数据解析
2.1 系统方式数据解析
-
SAX 解析步骤:
1、开始文档 - 准备工作
2、开始 "节点"
3、发现节点内部的内容,每一个节点,可能需要多次才能找完
4、结束 "节点"
5、结束文档 - 解析结束
以上步骤,2,3,4,会不断循环,一直到所有解析完成。
使用 NSXMLParser 对 XML 数据解析时,由上至下依次遍历每一个节点,遍历一个节点执行一次协议方法。
遵守协议 NSXMLParserDelegate
-
Objective-C
-
解析 xml 数据
// 解析本地数据时 xmlData 为读取的本地数据,解析网络数据时 xmlData 为网络下载的数据
NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithData:xmlData]; // 设置代理,需遵守协议 <NSXMLParserDelegate>
xmlParser.delegate = self; // 开始解析,一旦开始解析,后续的工作都是由代理方法来实现的
[xmlParser parse]; -
协议方法
// 打开文档,开始 XML 数据解析
- (void)parserDidStartDocument:(NSXMLParser *)parser { } // 开始遍历节点,包含根节点,"Element" 元素 节点
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict { /*
依次遍历节点名称和属性值 elementName: 节点名称
attributeDict:节点属性值
*/
} // 发现节点的内容
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string { /*
依次遍历节点内容 string:节点内容
*/
} // 结束节点遍历
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName { /*
依次结束节点遍历 elementName: 节点名称
*/
} // 结束文档,结束 XML 数据解析
- (void)parserDidEndDocument:(NSXMLParser *)parser { }
-
-
Swift
-
解析 xml 数据
// 解析本地数据时 xmlData 为读取的本地数据,解析网络数据时 xmlData 为网络下载的数据
let xmlParser = NSXMLParser(data: xmlData!) // 设置代理,需遵守协议 <NSXMLParserDelegate>
xmlParser.delegate = self // 开始解析,一旦开始解析,后续的工作都是由代理方法来实现的
xmlParser.parse() -
协议方法
// 打开文档,开始 XML 数据解析
func parserDidStartDocument(parser: NSXMLParser) { } // 开始遍历节点,包含根节点,"Element" 元素 节点
func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName
qName: String?, attributes attributeDict: [String: String]) { /*
依次遍历节点名称和属性值 elementName: 节点名称
attributeDict:节点属性值
*/
} // 发现节点的内容
func parser(parser: NSXMLParser, foundCharacters string: String) { /*
依次遍历节点内容 string:节点内容
*/
} // 结束节点遍历
func parser(parser: NSXMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) { /*
依次结束节点遍历 elementName: 节点名称
*/
} // 结束文档,结束 XML 数据解析
func parserDidEndDocument(parser: NSXMLParser) { }
-
2.2 系统方式解析转数据模型
-
Objective-C
-
VideoModel.h
@interface VideoModel : NSObject // 为了避免服务端返回的数值型数据是 null,可以把数值型的数据设置成 NSNumber
@property (nonatomic, copy) NSNumber *videoId; // copy 属性,在设置数值的时候,如果有一方是可变的,会默认做一次 copy 操作,会建立新的副本
@property (nonatomic, copy) NSString *name; // 在模型中对象全都是用 copy 属性会比较安全
@property (nonatomic, copy) NSNumber *length; @property (nonatomic, copy) NSString *videoURL;
@property (nonatomic, copy) NSString *imageURL;
@property (nonatomic, copy) NSString *desc;
@property (nonatomic, copy) NSString *teacher; @end -
VideoModel.m
// 开发调试输出
- (NSString *)description { return [NSString stringWithFormat:@"<%@ : %p> {\n\t\tvideoId: %@,\n\t\tname: %@,\n\t\tlength: %@,\n\t\tvideoURL: %@,\n\t\t
imageURL: %@,\n\t\tdesc: %@,\n\t\tteacher: %@\n\t}", [self class], self,
self.videoId, self.name, self.length, self.videoURL, self.imageURL, self.desc,
self.teacher];
} -
NSArray+LocaleLog.m
/*
Xcode 没有针对国际化语言做特殊处理,直接 Log 数组,只打印 UTF8 的编码,不能显示中文。重写这个方法,就能够解决输出问题,这个方法是专门为了本地话
提供的一个调试方法,只要重写,不需要导入头文件,程序中所有的 NSLog 数组的方法,都会被替代。
*/ - (NSString *)descriptionWithLocale:(id)locale { NSMutableString *strM = [NSMutableString stringWithString:@"(\n"]; [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { [strM appendFormat:@"\t%@", obj];
if (idx != self.count - 1) [strM appendFormat:@",\n"];
}]; [strM appendString:@"\n)"]; return strM;
} -
ViewController.m
// 数据源数组
@property (nonatomic, strong) NSMutableArray *videosSourceArray; // 数据模型
@property (nonatomic, strong) VideoModel *videoModel; // 数据模型的值
@property (nonatomic, strong) NSMutableString *videoModelElementValueString; - (NSMutableArray *)videosSourceArray {
if (_videosSourceArray == nil) {
_videosSourceArray = [[NSMutableArray alloc] init];
}
return _videosSourceArray;
} - (NSMutableString *)videoModelElementValueString {
if (_videoModelElementValueString == nil) {
_videoModelElementValueString = [[NSMutableString alloc] init];
}
return _videoModelElementValueString;
} // 解析 xml 数据 // 解析本地数据时 xmlData 为本地数据,解析网络数据时 xmlData 为网络下载数据
NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithData:xmlData]; // 设置代理,需遵守协议 <NSXMLParserDelegate>
xmlParser.delegate = self; // 开始解析,一旦开始解析,后续的工作都是由代理方法来实现的
[xmlParser parse]; // 协议方法 // 打开文档,开始 XML 数据解析
- (void)parserDidStartDocument:(NSXMLParser *)parser { // 为了保证能够多次解析,清空数据源数组
[self.videosSourceArray removeAllObjects];
} // 开始遍历节点
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict { // 判断是否是 video,如果是新建一个 videoModel 模型对象,并设置 videoId 的属性
if ([elementName isEqualToString:@"video"]) { // 设置 videoId 属性
self.videoModel = [[VideoModel alloc] init];
self.videoModel.videoId = @([attributeDict[@"videoId"] intValue]);
} // 清空上次 videoModel 内容的值
[self.videoModelElementValueString setString:@""];
} // 发现节点的内容
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string { // 拼接 videoModel 内容的值
[self.videoModelElementValueString appendString:string];
} // 结束节点遍历
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName { // 如果是 video,将模型添加到数据源数组
if ([elementName isEqualToString:@"video"]) { [self.videosSourceArray addObject:self.videoModel]; } else if (![elementName isEqualToString:@"videos"]) { // 如果不是最后一个节点(根节点) // KVC 设置数值,KVC 是一种间接设置数值的技术,被称为 cocoa 的大招
[self.videoModel setValue:self.videoModelElementValueString forKey:elementName];
}
} // 结束文档,结束 XML 数据解析
- (void)parserDidEndDocument:(NSXMLParser *)parser { NSLog(@"5. 结束文档 %@", self.videosSourceArray);
}
-
3、GDataXML-HTML 方式 XML 数据解析
3.1 添加 GDataXML-HTML
GDataXML-HTML 使用 ARC
-
Objective-C
// 添加第三方库文件
GDataXML-HTML // 在 TARGETS -> Builed Settings -> Search Paths -> Header Search Paths 中添加
// (libxml includes require that the target Header Search Paths contain)
/usr/include/libxml2 // 添加系统库文件 或者在 TARGETS -> Builed Settings -> Linking -> Other Linker Flags 中添加(and Other Linker Flags contain
libxml2.2.tbd -lxml2 // 包含头文件
#import "GDataXMLNode.h" -
Swift
// 添加第三方库文件
GDataXML-HTML // 在 TARGETS -> Builed Settings -> Search Paths -> Header Search Paths 中添加
// (libxml includes require that the target Header Search Paths contain)
/usr/include/libxml2 // 添加系统库文件 或者在 TARGETS -> Builed Settings -> Linking -> Other Linker Flags 中添加(and Other Linker Flags contain
libxml2.2.tbd -lxml2 // 创建名为 “项目名-Bridging-Header.h” 的桥接头文件,如:
SwiftXML-Bridging-Header.h // 在 TARGETS -> Build Setting -> Swift Compiler - Code generation -> Objective-C Bridging Header 中添加
// “项目名/项目名-Bridging-Header.h” 路径,如:
SwiftXML/SwiftXML-Bridging-Header.h // 在创建的桥接头文件中包含头文件
#import "GDataXMLNode.h"
3.2 GData path 数据解析
path 方法只能从外向内一层一层的解析 xml 数据。
-
Objective-C
// 实例化 GDataXMLDocument 对象
/*
GDataXMLDocument 是 GData 里专门用来解析 xml 数据的类
*/
GDataXMLDocument *xmlDocument = [[GDataXMLDocument alloc] initWithData:xmlData error:NULL]; // 解析根节点
/*
最外层的节点
*/
GDataXMLElement *rootElement = [xmlDocument rootElement]; // 解析子节点 // 获取子节点的个数
NSUInteger childElementCount = rootElement.childCount; // 获取当前节点的所有子节点
NSArray<GDataXMLElement *> *childrenElementsArray1 = rootElement.children;
GDataXMLElement *childrenElement1 = childrenElementsArray1[1]; // 获取当前节点指定名字的所有子节点,必须是当前节点的直接子节点
NSArray<GDataXMLElement *> *childrenElementsArray2 = [rootElement elementsForName:@"video"];
GDataXMLElement *childrenElement2 = childrenElementsArray2[1]; // 解析节点属性
/*
属性也是一种特殊的节点,为 GDataXMLNode 类型
*/
NSArray<GDataXMLNode *> *elementAttributeArray = [childrenElement1 attributes];
GDataXMLNode *attribute = elementAttributeArray[0]; // 获取节点值 // 将当前节点转换成字符串格式
NSString *elementString = childrenElement1.XMLString; // 获取当前节点的名字
NSString *elementName = childrenElement1.name; // 获取当前节点及子节点的内容值
NSString *elementValue = childrenElement1.stringValue; // 将当前属性转换成字符串格式
NSString *attributeString = attribute.XMLString; // 获取当前属性的名字
NSString *attributeName = attribute.name; // 获取当前属性的内容值
NSString *attributeValue = attribute.stringValue; -
Swift
// 实例化 GDataXMLDocument 对象
/*
GDataXMLDocument 是 GData 里专门用来解析 xml 数据的类
*/
let xmlDocument = try? GDataXMLDocument(data: xmlData) // 解析根节点
/*
最外层的节点
*/
let rootElement:GDataXMLElement = xmlDocument!.rootElement() // 解析子节点 // 获取子节点的个数
let childElementCount:UInt = rootElement.childCount() // 获取当前节点的所有子节点
let childrenElementsArray1:[AnyObject] = rootElement.children()
let childrenElement1:GDataXMLElement = childrenElementsArray1[1] as! GDataXMLElement // 获取当前节点指定名字的所有子节点,必须是当前节点的直接子节点
let childrenElementsArray2:[AnyObject] = rootElement.elementsForName("video")
let childrenElement2:GDataXMLElement = childrenElementsArray2[1] as! GDataXMLElement // 解析节点属性
/*
属性也是一种特殊的节点,为 GDataXMLNode 类型
*/
let elementAttributeArray:[AnyObject] = childrenElement1.attributes()
let attribute:GDataXMLNode = elementAttributeArray[0] as! GDataXMLNode // 获取节点值 // 将当前节点转换成字符串格式
let elementString:String = childrenElement1.XMLString() // 获取当前节点的名字
let elementName:String = childrenElement1.name() // 获取当前节点及子节点的内容值
let elementValue:String = childrenElement1.stringValue() // 将当前属性转换成字符串格式
let attributeString:String = attribute.XMLString() // 获取当前属性的名字
let attributeName:String = attribute.name() // 获取当前属性的内容值
let attributeValue:String = attribute.stringValue()
3.3 GData xpath 数据解析
xpath 方法可以指定路径解析 xml 数据。
-
Objective-C
// 实例化 GDataXMLDocument 对象
/*
GDataXMLDocument 是 GData 里专门用来解析 xml 数据的类
*/
GDataXMLDocument *xmlDocument = [[GDataXMLDocument alloc] initWithData:xmlData error:NULL]; // 1、绝对路径 // 指定绝对路径值,由根路径开始的完整路径
NSString *absoluteXpath = @"/videos/video/desc"; // 获取指定路径下的所有子节点
NSArray *childrenElementsArray1 = [xmlDocument nodesForXPath:absoluteXpath error:NULL]; for (GDataXMLElement *childrenElement in childrenElementsArray1) {
NSLog(@"childrenElement = %@", childrenElement.stringValue);
} // 2、相对路径 // 指定相对路径值,双斜杠加最后一级路径
NSString *relativelyXpath = @"//desc"; // 获取指定路径下的所有子节点
NSArray *childrenElementsArray2 = [xmlDocument nodesForXPath:relativelyXpath error:NULL]; for (GDataXMLElement *childrenElement in childrenElementsArray2) {
NSLog(@"childrenElement = %@", childrenElement.stringValue);
} -
Swift
// 实例化 GDataXMLDocument 对象
/*
GDataXMLDocument 是 GData 里专门用来解析 xml 数据的类
*/
let xmlDocument = try? GDataXMLDocument(data: xmlData) // 1、绝对路径 // 指定绝对路径值,由根路径开始的完整路径
let absoluteXpath = "/videos/video/desc" // 获取指定路径下的所有子节点
let childrenElementsArray1 = try? xmlDocument?.nodesForXPath(absoluteXpath) for childrenElement in childrenElementsArray1!! {
print((childrenElement as! GDataXMLElement).stringValue())
} // 2、相对路径 // 指定相对路径值,双斜杠加最后一级路径
let relativelyXpath = "//desc" // 获取指定路径下的所有子节点
let childrenElementsArray2 = try? xmlDocument?.nodesForXPath(relativelyXpath) for childrenElement in childrenElementsArray2!! {
print((childrenElement as! GDataXMLElement).stringValue())
}
3.4 GData 解析转数据模型
-
Objective-C
-
VideoModel.h
@interface VideoModel : NSObject // 为了避免服务端返回的数值型数据是 null,可以把数值型的数据设置成 NSNumber
@property (nonatomic, copy) NSNumber *videoId; // copy 属性,在设置数值的时候,如果有一方是可变的,会默认做一次 copy 操作,会建立新的副本
@property (nonatomic, copy) NSString *name; // 在模型中对象全都是用 copy 属性会比较安全
@property (nonatomic, copy) NSNumber *length; @property (nonatomic, copy) NSString *videoURL;
@property (nonatomic, copy) NSString *imageURL;
@property (nonatomic, copy) NSString *desc;
@property (nonatomic, copy) NSString *teacher; @end -
VideoModel.m
// 开发调试输出
- (NSString *)description { return [NSString stringWithFormat:@"<%@ : %p> {\n\t\tvideoId: %@,\n\t\tname: %@,\n\t\tlength: %@,\n\t\
tvideoURL: %@,\n\t\timageURL: %@,\n\t\tdesc: %@,\n\t\
tteacher: %@\n\t}", [self class], self, self.videoId,
self.name, self.length, self.videoURL, self.imageURL,
self.desc, self.teacher];
} -
NSArray+LocaleLog.m
/*
Xcode 没有针对国际化语言做特殊处理,直接 Log 数组,只打印 UTF8 的编码,不能显示中文。重写这个方法,就能够解决输出问题,
这个方法是专门为了本地话提供的一个调试方法,只要重写,不需要导入头文件,程序中所有的 NSLog 数组的方法,都会被替代。
*/ - (NSString *)descriptionWithLocale:(id)locale { NSMutableString *strM = [NSMutableString stringWithString:@"(\n"]; [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { [strM appendFormat:@"\t%@", obj];
if (idx != self.count - 1) [strM appendFormat:@",\n"];
}]; [strM appendString:@"\n)"]; return strM;
} -
ViewController.m
// 数据源数组
@property (nonatomic, strong) NSMutableArray *videosSourceArray; // 数据模型
@property (nonatomic, strong) VideoModel *videoModel; - (NSMutableArray *)videosSourceArray {
if (_videosSourceArray == nil) {
_videosSourceArray = [[NSMutableArray alloc] init];
}
return _videosSourceArray;
} // 解析 xml 数据 // 从本地文件中读取数据
NSData *xmlData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"XmlDataFile" ofType:@"xml"]]; // GDataXMLDocument 是 GData 里专门用来解析 xml 数据的类
GDataXMLDocument *xmlDocument = [[GDataXMLDocument alloc] initWithData:xmlData error:NULL]; // 解析根节点
for (GDataXMLElement *childElement in xmlDocument.rootElement.children) { self.videoModel = [[VideoModel alloc] init];
[self.videosSourceArray addObject:self.videoModel]; // 解析子节点属性值
for (GDataXMLNode *attribute in childElement.attributes) { [self.videoModel setValue:attribute.stringValue forKey:attribute.name];
} // 解析子节点的子节点
for (GDataXMLElement *propertyElement in childElement.children) { [self.videoModel setValue:propertyElement.stringValue forKey:propertyElement.name];
}
} NSLog(@"videosSourceArray: %@", self.videosSourceArray);
-
4、XML 和 JSON 两种数据交换格式的比较
目前,在 web 开发领域,主要的数据交换格式有 XML 和 JSON,对于 XML 相信每一个 web developer 都不会感到陌生; 相比之下,JSON 可能对于一些新步入开发领域的新手会感到有些陌生,也可能你之前已经听说过,但对于 XML 和 JSON 的不同之处可能会不怎么了解。对于在 Ajax 开发中,是选择 XML 还是 JSON,一直存在着争议,个人还是比较倾向于 JSON 的,虽然 JSON 才处于起步阶段,但我相信 JSON 最终会取代 XML 成为 Ajax 的首选,到时 Ajax 可能要更名为 Ajaj(Asynchronous JavaScript and JSON)了;
-
1、数据交换格式比较之关于 XML 和 JSON:
XML:extensible markup language,一种类似于 HTML 的语言,他没有预先定义的标签,使用 DTD(document type definition) 文档类型定义来组织数据;格式统一,跨平台和语言,早已成为业界公认的标准。具体的可以问 Google 或百度。相比之 JSON 这种轻量级的数据交换格式,XML 可以称为重量级的了。
JSON : JavaScript Object Notation 是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于 JavaScript Programming Language , Standard ECMA-262 3rd Edition – December 1999 的一个子集。JSON 采用完全独立于语言的文本格式,但是也使用了类似于 C 语言家族的习惯(包括 C, C++, C#, Java, JavaScript, Perl, Python 等)。这些特性使 JSON 成为理想的数据交换语言。
-
2、数据交换格式比较之关于轻量级和重量级:
轻量级和重量级是相对来说的,那么 XML 相对于 JSON 的重量级体现在哪呢?我想应该体现在解析上,XML 目前设计了两种解析方式:DOM 和 SAX;
DOM是把一个数据交换格式 XML 看成一个 DOM 对象,需要把 XML 文件整个读入内存,这一点上 JSON 和 XML 的原理是一样的,但是 XML 要考虑父节点和子节点,这一点上 JSON 的解析难度要小很多,因为 JSON 构建于两种结构:key/value,键值对的集合;值的有序集合,可理解为数组;
SAX 不需要整个读入文档就可以对解析出的内容进行处理,是一种逐步解析的方法。程序也可以随时终止解析。这样,一个大的文档就可以逐步的、一点一点的展现出来,所以 SAX 适合于大规模的解析。这一点,JSON 目前是做不到得。
所以,JSON 和 XML 的轻/重量级的区别在于:JSON 只提供整体解析方案,而这种方法只在解析较少的数据时才能起到良好的效果;而 XML 提供了对大规模数据的逐步解析方案,这种方案很适合于对大量数据的处理。
-
3、数据交换格式比较之关于数据格式编码及解析的难度:
在编码上,虽然 XML 和 JSON 都有各自的编码工具,但是 JSON 的编码要比 XML 简单,即使不借助工具,也可以写出 JSON 代码,但要写出好的 XML 代码就有点困难;与 XML 一样,JSON 也是基于文本的,且它们都使用 Unicode 编码,且其与数据交换格式 XML 一样具有可读性。
主观上来看,JSON 更为清晰且冗余更少些。JSON 网站提供了对 JSON 语法的严格描述,只是描述较简短。从总体来看,XML 比较适合于标记文档,而 JSON 却更适于进行数据交换处理。
在解析上,在普通的 web 应用领域,开发者经常为 XML 的解析伤脑筋,无论是服务器端生成或处理 XML,还是客户端用 JavaScript 解析 XML,都常常导致复杂的代码,极低的开发效率。
实际上,对于大多数 web 应用来说,他们根本不需要复杂的 XML 来传输数据,XML 宣称的扩展性在此就很少具有优势;许多 Ajax 应用甚至直接返回 HTML 片段来构建动态 web 页面。和返回 XML 并解析它相比,返回 HTML 片段大大降低了系统的复杂性,但同时缺少了一定的灵活性。同 XML 或 HTML 片段相比,数据交换格式 JSON 提供了更好的简单性和灵活性。在 web serivice 应用中,至少就目前来说 XML 仍有不可动摇的地位。