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網站

相關文章
相關標籤/搜索