iOS 网络编程:XML解析

1 XML文档结构 html

1.1 简介 git

      XML 指可扩展标记语言(eXtensible Markup Language)。XML 被设计用来传输和存储数据。其很是像HTML的标记语言,但与之不一样的是,XML是用来传输和存储数据;而HTML是用来显示数据;同时XML 标签没有被预约义,须要自行定义标签,而HTML的标签是有明确的语义的。 github

      XML 文档必须包含根元素。该元素是全部其余元素的父元素。XML 文档中的元素造成了一棵文档树。这棵树从根部开始,并扩展到树的最底端。全部的元素均可以有子元素。 数组

图 4 网络

 

 1 <?xml version= " 1.0 " encoding= " ISO-8859-1 "?>
 2 <bookstore>
 3     <book category= " CHILDREN ">
 4         <title>Harry Potter</title>
 5         <author>J K. Rowling</author>
 6         <year> 2005</year>
 7         <price> 29.99</price>
 8     </book>
 9     <book category= " WEB ">
10         <title>Learning XML</title>
11         <author>Erik T. Ray</author>
12         <year> 2003</year>
13         <price> 39.95</price>
14     </book>
15 </bookstore>

 

1.2 语法规则 架构

      XML 的语法规则很简单,且颇有逻辑。这些规则很容易学习,也很容易使用。XML文档的基本架构能够分为下面几个部分: 框架

1) 声明 less

    如上述的<?xml version="1.0" encoding="ISO-8859-1"?>就是XML的声明,它定义了XML文件的版本和使用的字符集,这里为1.0版,使用字符集为ISO-8859-1。 ide

2) 根元素 函数

    XML 文档必须有一个元素是全部其余元素的父元素。该元素称为根元素。

3) 子元素

    XML 元素指的是从(且包括)开始标签直到(且包括)结束标签的部分。一个元素能够包含:

  • 其余元素
  • 文本
  • 属性
  • 或混合以上全部...

4) 属性

    属性(Attribute)提供有关元素的额外信息,属性定义在开始标签之中,而且属性值必须被引号包围,不过单引号和双引号都可使用

<file type= " gif ">computer.gif</file>

 

5) 命名空间

    在 XML 中,元素名称是由开发者定义的,当两个不一样的文档使用相同的元素名时,就会发生命名冲突。为了不这种元素名称冲突,因此引入了命名空间,命名空间是在元素的开始标签的 xmlns 属性中定义的。命名空间声明的语法以下。xmlns:前缀="URI"

 1 <root>
 2 
 3 <h:table xmlns:h= " http://www.w3.org/TR/html4/ ">
 4 <h:tr>
 5 <h:td>Apples</h:td>
 6 <h:td>Bananas</h:td>
 7 </h:tr>
 8 </h:table>
 9 
10 <f:table xmlns:f= " http://www.w3cschool.cc/furniture ">
11 <f:name>African Coffee Table</f:name>
12 <f:width> 80</f:width>
13 <f:length> 120</f:length>
14 </f:table>
15 
16 </root>

      在上面的实例中,<table> 标签的 xmlns 属性定义了 h: 和 f: 前缀的合格命名空间。当命名空间被定义在元素的开始标签中时,全部带有相同前缀的子元素都会与同一个命名空间相关联。

 

1.3 注意规则

1) 全部的 XML 元素都必须有一个关闭标签

      在XML 中,省略关闭标签是非法的。全部元素都必须关闭标签,而且有两种:显示和非显示的

<p>This  is a paragraph.</p>
<br />

2) XML 标签对大小写敏感

     XML 标签对大小写敏感。标签 <Letter> 与标签 <letter> 是不一样的。必须使用相同的大小写来编写开始标签和关闭标签。

3) 实体引用

     在XML 中,一些字符拥有特殊的意义。若是您把字符 "<" 放在 XML 元素中,会发生错误,这是由于解析器会把它看成新元素的开始。为了不这个错误,请用实体引用来代替 "<" 字符:

    在XML中,有 5 个预约义的实体引用:

&lt;

<

less than

&gt;

>

greater than

&amp;

&

ampersand

&apos;

'

apostrophe

&quot;

"

quotation mark

4) XML 中的注释

     在 XML 中编写注释的语法与 HTML 的语法很类似。

<!-- This  is a comment -->

5) 在 XML 中,空格会被保留

    HTML 会把多个连续的空格字符裁减(合并)为一个,而在 XML 中,文档中的空格不会被删减。

6) XML 以 LF 存储换行

    在 Windows 应用程序中,换行一般以一对字符来存储:回车符(CR)和换行符(LF)。 在 Unix 和 Mac OSX 中,使用 LF 来存储新行。 在旧的 Mac 系统中,使用 CR 来存储新行。 XML 以 LF 存储换行。

 

2 XML文档解析

    解析XML文档时,目前有两种流行的模式:SAX和DOM。

2.1 解析模式

1) SAM模式

     SAM时一种基于事件驱动的解析模式。解析XML文档时,程序从上到下读取XML文档,若是遇到开始标签、结束标签和属性等,就会触发相应的事件。可是这种解析XML文件的方式有一个弊端,那就是只能读取XML文档,不能写入XML文档,它的优势是解析速度快适合大文件

2) DOM模式

      DOM模式将XML文档映射为一棵树状结构进行分析获取节点的内容以及相关属性,或是新增、删除和修改节点的内容。XML解析器在加载XML文件之后,DOM模式将XML文件的元素视为树状结构的节点,一次性读入到内存中。若是文档比较大,解析速度就会变慢,因此该模式适合小文件。可是在DOM模式中,有一点是SAX没法取代的,那就是DOM可以修改XML文档

 

2.2 解析框架

目前流行的解析框架有:

  • NSXMLParser框架:这是IOS自带解析框架,其使用的是SAM解析模式;
  • TBXML框架:这是第三方框架,使用DOM模式。

 

3 NSXMLParser框架

3.1 简介

      NSXMLParser框架采用SAM模式进行解析,即其是基于事件驱动的方式进行解析。NSXMLParser框架的解析工做是交给了NSXMLParserDelegate实现类去完成。在委托中定义了不少回调方法,当进行SAX解析XML文件过程当中,遇到开始标签结束标签文档开始文档结束字符串等XML语法时就会触发相应的回调方法。

     其中主要的有5个回调方法,其它相关的回调方法能够参考Apple帮助文档:

  • -parserDidStartDocument:(NSXMLParser *)parser:在文档开始的时候触发
  • -parserDidEndDocument:(NSXMLParser *)parser:在文档结束时触发
  • -parser:didStartElement:namespaceURI:qualifiedName:attributes:遇到开始标签时触发,其中namespaceURI是命名空间,attributes是字典类型的属性集合
  • - parser:didEndElement:namespaceURI:遇到结束标签时触发
  • - parser:foundCharacters:遇到字符串时触发

 

3.2 第一个应用程序

1) XML文件:note.xml

 1 <?xml version= " 1.0 " encoding= " UTF-8 "?>
 2 <Notes>
 3   <Note id= " 1 ">
 4     <CDate> 2012- 12- 21</CDate>
 5     <Content>早上8点钟到公司</Content>
 6     <UserID>tony</UserID>
 7   </Note>
 8   <Note id= " 2 ">
 9     <CDate> 2012- 12- 22</CDate>
10     <Content>发布iOSBook1</Content>
11     <UserID>tony</UserID>
12   </Note>
13   <Note id= " 3 ">
14     <CDate> 2012- 12- 23</CDate>
15     <Content>发布iOSBook2</Content>
16     <UserID>tony</UserID>
17   </Note>
18   <Note id= " 4 ">
19     <CDate> 2012- 12- 24</CDate>
20     <Content>发布iOSBook3</Content>
21     <UserID>tony</UserID>
22   </Note>
23   <Note id= " 5 ">
24     <CDate> 2012- 12- 25</CDate>
25     <Content>发布2016奥运会应用iPhone版本</Content>
26     <UserID>tony</UserID>
27   </Note>
28   <Note id= " 6 ">
29     <CDate> 2012- 12- 26</CDate>
30     <Content>发布2016奥运会应用iPad版本</Content>
31     <UserID>tony</UserID>
32   </Note>
33 </Notes>

 

2) 委托类头文件

1 @interface ViewController : UIViewController <NSXMLParserDelegate>
2 
3 @property (strong,nonatomic) NSMutableArray *notes;   // 解析出的数据内部是字典类型的数组
4  @property (strong,nonatomic) NSString *currentTagName;   // 当前标签的名字
5 
6 -( void)start;   // 自定义方法:开始解析
7 
8 @end

 

3) 委托类源文件

 1 @implementation ViewController
 2 - ( void)viewDidLoad {
 3     [super viewDidLoad];
 4     [self start];
 5 }
 6 
 7 -( void)start
 8 {
 9     NSString* path = [[NSBundle mainBundle] pathForResource: @" Notes " ofType: @" xml "];  // 获取XML文件路径
10      NSURL *url = [NSURL fileURLWithPath:path];
11     
12     NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:url];  // 建立NSXMLParser 对象
13      parser. delegate = self;  // 设置委托对象
14      [parser parse];   // 开始进行解析
15      NSLog( @" 解析完成... ");
16 }
17 
18 - ( void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName  // 遇到一个开始标签时候触发
19    namespaceURI:(NSString *)namespaceURI
20   qualifiedName:(NSString *)qualifiedName
21   attributes:(NSDictionary *)attributeDict
22 {
23     _currentTagName = elementName;
24      if ([_currentTagName isEqualToString: @" Note "]) {
25         NSString *_id = [attributeDict objectForKey: @" id "];
26         NSMutableDictionary *dict = [NSMutableDictionary  new];
27         [dict setObject:_id forKey: @" id "];
28         [_notes addObject:dict];   // 为新的标签建立一个对应的字典,并添加到数组中
29      }   
30 }
31 
32 - ( void)parser:(NSXMLParser *)parser foundCharacters:(NSString *) string   // 遇到字符串时候触发
33  {
34      // 替换回车符和空格
35       string =[ string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
36      if ([ string isEqualToString: @""]) {
37          return;
38     }
39      // NSLog(string);
40      NSMutableDictionary *dict = [_notes lastObject]; // 获取数组中最后一个元素(即当前标签所对应的字典)
41       if ([_currentTagName isEqualToString: @" CDate "] && dict) {
42         [dict setObject: string forKey: @" CDate "];  // 往字典中添加键值对。
43      }
44      if ([_currentTagName isEqualToString: @" Content "] && dict) {
45         [dict setObject: string forKey: @" Content "];
46     }
47      if ([_currentTagName isEqualToString: @" UserID "] && dict) {
48         [dict setObject: string forKey: @" UserID "];
49     }
50 }
51 
52 - ( void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName  // 遇到结束标签时候出发
53    namespaceURI:(NSString *)namespaceURI
54  qualifiedName:(NSString *)qName;
55 {
56     self.currentTagName = nil;
57 }
58 - ( void)parserDidStartDocument:(NSXMLParser *)parser   // 文档开始的时候触发
59  {
60     _notes = [NSMutableArray  new];
61     NSLog( @" 文档开始的时候触发 ");
62 }
63 - ( void)parserDidEndDocument:(NSXMLParser *)parser  // 遇到文档结束时候触发
64  {
65     [[NSNotificationCenter defaultCenter] postNotificationName: @" reloadViewNotification "  object:self.notes userInfo:nil];
66     self.notes = nil;
67 }
68 - ( void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError   // 文档出错的时候触发
69  {
70     NSLog( @" %@ ",parseError);
71 }
72 @end

 

3.3 XML解析过程

      使用NSXMLParser解析XML文件很是简单,只需实现NSXMLParserDelegate协议相应的方法便可,以下图所示:

      1) 首先,实现NSXMLParserDelegate协议,并在该协议相应的回调方法中,实现具体解析内容;

      2) 而后,获取XML文件的路径;

      3) 接着,调用NSXMLParser类的构造函数initWithContentsOFURL来建立NSXMLParser对象;

      4) 最后,给NSXMLParser对象指定委托对象,最后调用该对象的Parse方法启动解析;

 

 

4 TBXML解析

4.1 简介

      TBXML是轻量级的DOM模式解析库,它只能读取XML文档,不能写XML文档,可是解析XML是最快的。

其中TBXML框架是一个开源项目,其相关的网络地址为:

从下载的TBXML.h文件中,能够 了解其内部的结构和提供的功能方法:

 1  //  ================================================================================================
 2  //   Structures
 3  //  ================================================================================================
 4  /* * The TBXMLAttribute structure holds information about a single XML attribute. The structure holds the attribute name, value and next sibling attribute. This structure allows us to create a linked list of attributes belonging to a specific element.
 5    */
 6 typedef  struct _TBXMLAttribute {
 7      char * name;
 8      char * value;
 9      struct _TBXMLAttribute * next;
10 } TBXMLAttribute;
11 
12  /* * The TBXMLElement structure holds information about a single XML element. The structure holds the element name & text along with pointers to the first attribute, parent element, first child element and first sibling element. Using this structure, we can create a linked list of TBXMLElements to map out an entire XML file. */
13 typedef  struct _TBXMLElement {
14      char * name;
15      char * text;
16     
17     TBXMLAttribute * firstAttribute;
18      struct _TBXMLElement * parentElement;
19 
20      struct _TBXMLElement * firstChild;
21      struct _TBXMLElement * currentChild;
22     
23      struct _TBXMLElement * nextSibling;
24      struct _TBXMLElement * previousSibling;
25 } TBXMLElement;
26 
27  /* * The TBXMLElementBuffer is a structure that holds a buffer of TBXMLElements. When the buffer of elements is used, an additional buffer is created and linked to the previous one. This allows for efficient memory allocation/deallocation elements.
28    */
29 typedef  struct _TBXMLElementBuffer {
30     TBXMLElement * elements;
31      struct _TBXMLElementBuffer * next;
32      struct _TBXMLElementBuffer * previous;
33 } TBXMLElementBuffer;
34 
35  /* * The TBXMLAttributeBuffer is a structure that holds a buffer of TBXMLAttributes. When the buffer of attributes is used, an additional buffer is created and linked to the previous one. This allows for efficient memeory allocation/deallocation of attributes.
36    */
37 typedef  struct _TBXMLAttributeBuffer {
38     TBXMLAttribute * attributes;
39      struct _TBXMLAttributeBuffer * next;
40      struct _TBXMLAttributeBuffer * previous;
41 } TBXMLAttributeBuffer;

因为提供的方法不方便粘贴进入文档,因此具体方法能够参考TBXML.h文件。

 

4.2 第一个应用程序

4.2.1 环境配置

      由于TBXML框架是第三方的开发库,因此在Xcode中使用须要手动添加相应的文件并相应配置一些内容。具体过程为:

1) 添加以下四个TBXML所依赖的Framwork和库

  • Foundation.framework
  • UIKit.framework
  • CoreGraphics.framework
  • Libz.tbd

图 6

图 7

2) 在Object-C语言中,须要在工程中添加预编译头问津啊PrefixHeader.pch,并在该文件中添加相应的两行代码。

图 8

图 9

 

3) 将在gidhut网站中下载的文件夹添加到工程中,如图 10所示是已经添加完成的项目目录。

图 10

4.2.2 示例代码

      因为解析XML文件必须有具体的XML文件内容格式,因此本例仍是以2.3.2小节的Note.xml文件进行解析。其中解析类的.h文件以下所示。

 1 #import <UIKit/UIKit.h>
 2 #import  " TBXML.h "  // 必须引入的头文件
 3 
 4 @interface ViewController : UIViewController 
 5 
 6  // 解析出的数据内部是字典类型
 7  @property (strong,nonatomic) NSMutableArray *notes;
 8 
 9  // 开始解析方法
10  -( void)startTBXMLParser;
11 
12 @end

 

而具体的.m文件内容以下:

 1 -( void)startTBXMLParser
 2 {
 3     _notes = [NSMutableArray  new];
 4     TBXML* tbxml = [[TBXML alloc] initWithXMLFile: @" Notes.xml " error:nil]; // 读取XML文件,并以树形结构建立对象。
 5      TBXMLElement * root = tbxml.rootXMLElement;   // 获取XML树形的根节点
 6      
 7       //  if root element is valid
 8       if (root) {
 9         TBXMLElement * noteElement = [TBXML childElementNamed: @" Note " parentElement:root]; // 获取第一个子元素
10           while ( noteElement != nil) {
11             NSMutableDictionary *dict = [NSMutableDictionary  new];
12             TBXMLElement *CDateElement = [TBXML childElementNamed: @" CDate " parentElement:noteElement];
13              if ( CDateElement != nil) {
14                 NSString *CDate = [TBXML textForElement:CDateElement];   // 获取子元素的内容。
15                  [dict setValue:CDate forKey: @" CDate "];
16             }
17             
18             TBXMLElement *ContentElement = [TBXML childElementNamed: @" Content " parentElement:noteElement];
19              if ( ContentElement != nil) {
20                 NSString *Content = [TBXML textForElement:ContentElement];
21                 [dict setValue:Content forKey: @" Content "];
22                  // NSLog(Content);
23              }
24             
25             TBXMLElement *UserIDElement = [TBXML childElementNamed: @" UserID " parentElement:noteElement];
26              if ( UserIDElement != nil) {
27                 NSString *UserID = [TBXML textForElement:UserIDElement];
28                 [dict setValue:UserID forKey: @" UserID "];
29             }
30             
31              // 得到ID属性
32              NSString *_id = [TBXML valueOfAttributeNamed: @" id " forElement:noteElement error:nil];
33 [dict setValue:_id forKey: @" id "];
34 [_notes addObject:dict];
35             noteElement = [TBXML nextSiblingNamed: @" Note " searchFromElement:noteElement];
36         }
37     }
38     
39     NSLog( @" 解析完成... ");
40     [[NSNotificationCenter defaultCenter] postNotificationName: @" reloadViewNotification "  object:self.notes userInfo:nil];
41     self.notes = nil;
42 }

 

4.3 XML解析过程

      利用TBXML框架解析XML的过程是以树状的结构加载XML文件,树中的每一个节点都是XML的一个元素,因此就能够遍历树中的每一个节点(XML的元素),从而便可解析整棵树(整个XML文件)。

4.3.1 加载XML文件

      XML文件在TBXML框架中以TBXML对象表示,而且该框架提供了多种方式加载XML文件。

  • 文件名方式:  
TBXML * tbxml = [[TBXML tbxmlWithXMLFile: @" books.xml "] retain];
  • 扩展名方式:
TBXML * tbxml = [[TBXML tbxmlWithXMLFile: @" books " fileExtension: @" xml "] retain];
  •  NSData对象方式:
TBXML * tbxml = [[TBXML tbxmlWithXMLData:myXMLData] retain];
  •  URL路径方式:
TBXML * tbxml = [[TBXML tbxmlWithURL:[NSURL URLWithString:  @" http://www.w3schools.com/XML/note.xml "]] retain];
  •  XML 字符方式:
TBXML * tbxml = [[TBXML tbxmlWithXMLString: @" <root><elem1 attribute1=\ "elem1-attribute1\ " /><elem2 attribute2=\"attribute2\"/></root> "] retain];

 

4.3.2 获取XML元素

      在XML文件中全部的节点都是元素,最顶端的是根元素,其它节点都是根元素的子元素(直接或是间接)。

  • 获取根元素

每一个XML文档都只有一个根元素,因此能够获取TBXML对象的rootXMLElement成员变量,如:

TBXMLElement * rootXMLElement = tbxml.rootXMLElement;
  • 获取子元素

    TBXML对象提供一个childElementNamed: parentElement:方法获取子元素。其中该方法经过指定相应的父元素对象和子元素标签名来查找子元素对象,其从文档中查找第一个符合条件的对象。

[TBXML childElementNamed: @" author " parentElement:root]

 

4.3.3 获取XML属性

      在XML文件中每一个元素均可能有元素,因此能够利用TBXML对象的valueOfAttributeNamed: forElement:方法来查询相应元素的属性。

NSString *name = [TBXML valueOfAttributeNamed: @" name " forElement:author];

 

4.3.4 获取XML元素的文本

     在获取XML元素对象后,就能够经过TBXML对象的textForElement:方法获取指定元素的文本值。

NSString *description = [TBXML textForElement:descriptionElement];

 

4.3.5 遍历不知名的元素或属性

      因为XML文档是一个树状的结构,每一个元素都有指向其第一个子元素的指针firstChild,从而可以获取子元素。同时全部子元素都造成一条链表,全部能够经过元素的nextSibling指针,获取其兄弟元素。

      对于XML元素中的属性,也能够经过一样的方式进行遍历。能够经过TBXMLElement对象的firstAttribute指针获取第一个元素,而后经过TBXMLAttribute属性对象的next指针获取下一个兄弟属性。

 1 - ( void)loadUnknownXML 
 2 {
 3     tbxml = [[TBXML tbxmlWithXMLFile: @" books.xml "] retain]; //  Load and parse the books.xml file
 4 
 5      if (tbxml.rootXMLElement)
 6         [self traverseElement:tbxml.rootXMLElement];
 7 
 8     [tbxml release]; //  release resources
 9  }
10 
11 - ( void) traverseElement:(TBXMLElement *)element 
12 {
13  do 
14 {
15         NSLog( @" %@ ",[TBXML elementName:element]); //  Display the name of the element
16 
17           //  Obtain first attribute from element
18          TBXMLAttribute * attribute = element->firstAttribute;
19      while (attribute)
20     {
21              //  Display name and value of attribute to the log window
22              NSLog( @" %@->%@ = %@ ",   [TBXML elementName:element],
23                                 [TBXML attributeName:attribute],
24                                 [TBXML attributeValue:attribute]);
25 
26             attribute = attribute->next; //  Obtain the next attribute
27          }
28 
29          //  if the element has child elements, process them
30           if (element->firstChild) 
31                 [self traverseElement:element->firstChild]; // 递归查询子元素
32 
33     }  while ((element = element->nextSibling));   //  Obtain next sibling element
34  }

 

5 参考文献

      [1] Runoob.com;

      [2] Introduction to Event-Driven XML Programming Guide for Cocoa;(simply need to parse XML and extract information from an existing source of XML。NSXMLParser )

      [3] Introduction to Tree-Based XML Programming Guide for Cocoa;

      [4] TBXML网站

相关文章
相关标签/搜索