iOS開發指南-XML篇

本篇文章的所用的示例項目Demo可以在以下幾個地方下載

#一. XML文檔結構

##1.關於XML
XML是一種自描述的數據交換格式,多年來一直用於各種計算機語言中.

在讀寫XML時,我們需要了解XML文檔結構.

XML文檔結構需要遵守一定的格式規範.雖然其形式上與HTML很相似,但是它有着嚴格的語法規則.只有嚴格按照規範編寫XML文檔,纔是有效的文檔,格式良好的XML文檔.

##2.XML示例

<?xml version="1.0" encoding="UTF-8"?>
<Root xmlns="Provinces.xml">
  <!--省份XML-->
  <Province ID="1" ProvinceName="北京市">北京市</Province>
  <Province ID="2" ProvinceName="天津市">天津市</Province>
  <Province ID="3" ProvinceName="河北省">河北省</Province>
  <Province ID="4" ProvinceName="山西省">山西省</Province>
  <Province ID="5" ProvinceName="內蒙古自治區">內蒙古自治區</Province>
  <Province ID="6" ProvinceName="遼寧省">遼寧省</Province>
  <Province ID="7" ProvinceName="吉林省">吉林省</Province>
  <Province ID="8" ProvinceName="黑龍江省">黑龍江省</Province>
  <Province ID="9" ProvinceName="上海市">上海市</Province>
  <Province ID="10" ProvinceName="江蘇省">江蘇省</Province>
  <Province ID="11" ProvinceName="浙江省">浙江省</Province>
  <Province ID="12" ProvinceName="安徽省">安徽省</Province>
  <Province ID="13" ProvinceName="福建省">福建省</Province>
  <Province ID="14" ProvinceName="江西省">江西省</Province>
  <Province ID="15" ProvinceName="山東省">山東省</Province>
  <Province ID="16" ProvinceName="河南省">河南省</Province>
  <Province ID="17" ProvinceName="湖北省">湖北省</Province>
  <Province ID="18" ProvinceName="湖南省">湖南省</Province>
  <Province ID="19" ProvinceName="廣東省">廣東省</Province>
  <Province ID="20" ProvinceName="廣西壯族自治區">廣西壯族自治區</Province>
  <Province ID="21" ProvinceName="海南省">海南省</Province>
  <Province ID="22" ProvinceName="重慶市">重慶市</Province>
  <Province ID="23" ProvinceName="四川省">四川省</Province>
  <Province ID="24" ProvinceName="貴州省">貴州省</Province>
  <Province ID="25" ProvinceName="雲南省">雲南省</Province>
  <Province ID="26" ProvinceName="西藏自治區">西藏自治區</Province>
  <Province ID="27" ProvinceName="陝西省">陝西省</Province>
  <Province ID="28" ProvinceName="甘肅省">甘肅省</Province>
  <Province ID="29" ProvinceName="青海省">青海省</Province>
  <Province ID="30" ProvinceName="寧夏回族自治區">寧夏回族自治區</Province>
  <Province ID="31" ProvinceName="新疆維吾爾自治區">新疆維吾爾自治區</Province>
  <Province ID="32" ProvinceName="香港特別行政區">香港特別行政區</Province>
  <Province ID="33" ProvinceName="澳門特別行政區">澳門特別行政區</Province>
  <Province ID="34" ProvinceName="臺灣省">臺灣省</Province>
</Root>

##3.XML基本架構
XML基本架構可以分爲以下幾個部分:

###(1)聲明
在上面的示例中,

<?xml version="1.0" encoding="UTF-8"?>

就是XML的聲明,它定義了XML文件的版本和使用的字符編碼,這裏爲1.0版,使用中文UTF-8字符編碼.

###(2)根元素
在上面的示例中,

  • Root是XML文件的根元素,
  • <Root>是根元素的開始標籤,
  • </Root>是根元素的結束標籤.

根元素只能有一個,且開始標籤與結束標籤必須一致.

###(3)子元素
在上面的示例中,
ProvinceRoot的子元素.
所有的元素都要有開始標籤和結束標籤,且必須一致.

如果開始標籤和結束標籤之間沒有內容,可以寫成

<Province/>

這稱爲空標籤.

###(4)屬性
屬性定義在開始標籤中.

在上面的實例中,其中

  • ID=“1”
  • ProvinceName=“北京市”

Province元素的兩個屬性.ID爲屬性名,1爲屬性值.

屬性值必須放置在雙引號或單引號之間.且一個元素不能有兩個或以上同名的屬性.

###(5)命令空間
用於XML文檔提供名字唯一的元素和屬性
xmlns開頭的內容都屬於命名空間

###(6)限定名
由命名空間引出的概念,定義了元素和屬性的合法標識符.
限定名通常在XML文檔中用作特定元素或屬性引用.

#二. XML解析
XML文檔操作通常有,讀入XML文檔並分析的過程稱爲解析

##1. XML 2種解析模式

解析XML文檔時,目前有2種比較流行的模式

  • SAX
  • DOM

(1). SAX模式

①SAX原理
SAX是一種基於事件驅動的解析模式。解析XML文檔時,程序從上到下讀取XML文檔,如果遇到開始標籤、結束標籤和屬性等,就會觸發相應的事件。

②SAX優點
SAX的優點就是解析速度快,iOS重點推薦使用SAX模式解析

③SAX缺點
SAX只能讀取XML文檔,不能寫入XML文檔

(2). DOM模式

①DOM原理
DOM模式是將XML文檔作爲一顆樹狀結構進行分析,獲取節點的內容以及相關屬性,或是新增,刪除和修改節點的內容.
XML解析器在加載XML文件以後,DOM模式會將XML文件的元素視爲一個樹狀結構的節點,一次性讀入到內存中.

②DOM優點
DOM能夠修改XML文檔

③DOM缺點
因爲DOM模式會將XML文件的元素視爲一個樹狀結構的節點,一次性讀入到內存中.
所以如果文檔比較大,解析速度就會變慢.

##2. iOS SDK中提供的2個XML框架
iOS SDK中提供了2個XML的框架

  • NSXML
  • libxml2

###(1)NSXML
它是基於Objective-C語言SAX解析框架,是iOS SDK默認的XML解析框架,不支持DOM模式

###(2)libxml2
http://xmlsoft.org

它是基於C語言的XML解析器,被蘋果整合在iOS SDK中,支持SAXDOM模式

##3. iOS解析XML第三方框架

iOS解析XML,也有很多第三方的框架可以使用

###1. TBXML

  • 它是輕量級的DOM模式解析庫
  • 不支持XML文檔驗證和XPath,只能讀取XML文檔,不能寫XML文檔
  • 但是解析XML是最快的
  • 從解析性能上來看,NSXML和TBXML都非常優秀,但是使用NSXML編程比較麻煩,而TBXML就簡單多了

###2. TouchXML

  • 它是基於DOM模式的解析庫.
  • 與TBXML類似,只能讀取XML文檔,不能寫XML文檔

###3. KissXML

  • 它是基於DOM模式的解析庫,基於TouchXML.
  • 與TouchXML不同的是,可以寫入XML文檔

###3. TinyXML

  • 它是基於C++語言的DOM模式的解析庫.
  • 可以讀寫XML文檔,不支持XPath

###4. GDataXML

  • 它是基於DOM模式的解析庫.
  • 由Google開發,可以讀寫XML文檔,支持XPath查詢.

##4. 系統及第三方XML框架解析講解

我們以下面的產品信息的XML文件作爲示例,使用不同的框架解析進行代碼講解

product.xml

<?xml version="1.0" encoding="utf-8" ?>
<data xmlns="product.xml">	
	<product id="DB1" productName="多層實木複合柚木地板" typeName="實木複合地板" brand="KENTIER/肯帝亞" price="238">
		<info id="1" label="產品詳情">
			<items name="特性01">product/imgs/DB1/trait/1.jpg</items>
			<items name="特性02">product/imgs/DB1/trait/2.jpg</items>
			<items name="特性03">product/imgs/DB1/trait/3.jpg</items>
			<items name="特性04">product/imgs/DB1/trait/4.jpg</items>
			<items name="特性05">product/imgs/DB1/trait/5.jpg</items>
			<items name="特性06">product/imgs/DB1/trait/6.jpg</items>
			<items name="特性07">product/imgs/DB1/trait/7.jpg</items>
			<items name="特性08">product/imgs/DB1/trait/8.jpg</items>
			<items name="特性09">product/imgs/DB1/trait/9.jpg</items>
		</info>		
	</product>

	<product id="DB2" productName="肯帝亞15mm 強化複合地板" typeName="強化複合地板" brand="KENTIER/肯帝亞" price="59">
		<info id="1" label="產品詳情">
			<items name="特性01">product/imgs/DB2/trait/1.jpg</items>
			<items name="特性02">product/imgs/DB2/trait/2.jpg</items>
			<items name="特性03">product/imgs/DB2/trait/3.jpg</items>
			<items name="特性04">product/imgs/DB2/trait/4.jpg</items>
			<items name="特性05">product/imgs/DB2/trait/5.jpg</items>
			<items name="特性06">product/imgs/DB2/trait/6.jpg</items>
			<items name="特性07">product/imgs/DB2/trait/7.jpg</items>
			<items name="特性08">product/imgs/DB2/trait/8.jpg</items>
			<items name="特性09">product/imgs/DB2/trait/9.jpg</items>
			<items name="特性10">product/imgs/DB2/trait/10.jpg</items>
		</info>		
	</product>
</data>

###(1).使用NSXML解析,SAX模式

① NSXML是iOS SDK自帶的,也是蘋果默認的解析框架,採用SAX模式解析,是SAX解析模式的代表.
② NSXML框架的核心是NSXMLParser及其NSXMLParserDelegate,其中主要的解析工作是在其代理回調方法中實現的.其最常用的爲以下6個方法:

// 1. 開始解析文檔時調用
- (void)parserDidStartDocument:(NSXMLParser *)parser;

// 2. 遇到一個元素開始標籤時調用,其中attributes是字典類型的屬性集合.
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary<NSString *,NSString *> *)attributeDict;

// 3. 解析開始標籤與結束標籤值時,遇到字符串調用
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string;

// 4. 元素結束標籤時調用
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName;

// 5. 文檔解析結束時調用
- (void)parserDidEndDocument:(NSXMLParser *)parser;

// 6. 解析出錯時調用,並中斷解析
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError;

其上的方法調用的先後順序如下

XML解析器NSXMLParser1.parserDidStartDocument:2.parser:didStartElement:namespaceURI:qualifiedName:attributes:3.parser:foundCharacters:4.parser:didEndElement:namespaceURI:qualifiedName:5.parserDidEndDocument:XML解析器NSXMLParser

③ 爲了程序的耦合性和簡潔性,我們在NSXMLParser基礎封裝一個SXXMLParser類,提煉出最常用的屬性和方法,代碼如下

SXXMLParser.h

//
//  SXXMLParser.h
//  XMLDemo
//
//  Created by Story5 on 7/19/16.
//  Copyright © 2016 Story5. All rights reserved.
//

#import <Foundation/Foundation.h>

#pragma mark - protocol
@class SXXMLParser;
@protocol SXXMLParserDelegate <NSObject>

@optional

- (void)sxXMLParserDidStartDocument:(SXXMLParser *)parser;
- (void)sxXMLParser:(SXXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary<NSString *, NSString *> *)attributeDict;
- (void)sxXmlParser:(SXXMLParser *)parser foundCharacters:(NSString *)string element:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qulifiedName:(NSString *)qName;
//- (void)sxXMLParser:(SXXMLParser *)parser foundCharacters:(NSString *)string;
- (void)sxXMLParser:(SXXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName;
- (void)sxXMLParserDidEndDocument:(SXXMLParser *)parser;

- (void)sxXMLParser:(SXXMLParser *)parser parseErrorOccurred:(NSError *)parseError;

@end


#pragma mark - interface
@interface SXXMLParser : NSObject 

@property (nonatomic, assign) id <SXXMLParserDelegate> delegate;

/**
 *  SXXMLParser init
 */
- (instancetype)initWithContentsOfURL:(NSURL *)url;
- (instancetype)initWithData:(NSData *)data;
- (instancetype)initWithStream:(NSInputStream *)stream;

/**
 *  parser state
 */
- (BOOL)parse;

- (void)abortParsing;
@property (nonatomic, readonly, copy) NSError *parserError;

/**
 *  current property
 */
@property (nonatomic, readonly, strong) NSString *currentElementName;
@property (nonatomic, readonly, strong) NSString *currentNamespaceURI;
@property (nonatomic, readonly, strong) NSString *currentQName;
@property (nonatomic, readonly, strong) NSDictionary *currentAttributeDict;

@end

#pragma mark - Category (SXXMLParserLocatorAdditions)
// Once a parse has begun, the delegate may be interested in certain parser state. These methods will only return meaningful information during parsing, or after an error has occurred.
@interface SXXMLParser (SXXMLParserLocatorAdditions)

@property (readonly, copy) NSString *publicID;
@property (readonly, copy) NSString *systemID;
@property (readonly) NSInteger lineNumber;
@property (readonly) NSInteger columnNumber;

@end

SXXMLParser.m

//
//  SXXMLParser.m
//  XMLDemo
//
//  Created by Story5 on 7/19/16.
//  Copyright © 2016 Story5. All rights reserved.
//

#import "SXXMLParser.h"

@interface SXXMLParser ()<NSXMLParserDelegate>

@property (nonatomic,retain) NSXMLParser *xmlParser;

@end

@implementation SXXMLParser

#pragma mark - init
// initializes the parser with the specified URL.
- (instancetype)initWithContentsOfURL:(NSURL *)url
{
    self = [super init];
    if (self) {
     
        NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:url];
        parser.delegate = self;
        parser.shouldProcessNamespaces = YES;
        _xmlParser = parser;
    }
    
    return self;
}

// create the parser from data
- (instancetype)initWithData:(NSData *)data
{
    self = [super init];
    if (self) {
        
        NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
        parser.delegate = self;
        parser.shouldProcessNamespaces = YES;
        _xmlParser = parser;
    }
    
    return self;
    
}

//create a parser that incrementally pulls data from the specified stream and parses it.
- (instancetype)initWithStream:(NSInputStream *)stream
{
    self = [super init];
    if (self) {
        
        NSXMLParser *parser = [[NSXMLParser alloc] initWithStream:stream];
        parser.delegate = self;
        parser.shouldProcessNamespaces = YES;
        _xmlParser = parser;
    }
    
    return self;

}

#pragma mark - parse State
// called to start the event-driven parse. Returns YES in the event of a successful parse, and NO in case of error.
- (BOOL)parse
{
    return [_xmlParser parse];
}

// called by the delegate to stop the parse. The delegate will get an error message sent to it.
- (void)abortParsing
{
    [_xmlParser abortParsing];
}

// can be called after a parse is over to determine parser state.
- (NSError *)parserError
{
    return _xmlParser.parserError;
}

#pragma mark - Category (SXXMLParserLocatorAdditions)
- (NSString *)publicID
{
    return _xmlParser.publicID;
}

- (NSString *)systemID
{
    return _xmlParser.systemID;
}

- (NSInteger)lineNumber
{
    return _xmlParser.lineNumber;
}

- (NSInteger)columnNumber
{
    return _xmlParser.columnNumber;
}

#pragma mark - NSXMLParserDelegate
// sent when the parser begins parsing of the document.
- (void)parserDidStartDocument:(NSXMLParser *)parser
{
    if (_delegate && [_delegate respondsToSelector:@selector(sxXMLParserDidStartDocument:)]) {
        [_delegate performSelector:@selector(sxXMLParserDidStartDocument:) withObject:self];
    }
}

// sent when the parser finds an element start tag.
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary<NSString *,NSString *> *)attributeDict
{
    _currentElementName = elementName;
    _currentNamespaceURI = namespaceURI;
    _currentQName = qName;
    _currentAttributeDict = attributeDict;
    
    if (_delegate && [_delegate respondsToSelector:@selector(sxXMLParser:didStartElement:namespaceURI:qualifiedName:attributes:)]) {
        [_delegate sxXMLParser:self didStartElement:elementName namespaceURI:namespaceURI qualifiedName:qName attributes:attributeDict];
    }
}

// This returns the string of the characters encountered thus far. You may not necessarily get the longest character run. The parser reserves the right to hand these to the delegate as potentially many calls in a row to -parser:foundCharacters:
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
    //替換回車符和空格
    string = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
    if ([string isEqualToString:@""]) {
        return;
    }
    
    if (_delegate && [_delegate respondsToSelector:@selector(sxXmlParser:foundCharacters:element:namespaceURI:qulifiedName:)]) {
        
        [_delegate sxXmlParser:self foundCharacters:string element:_currentElementName namespaceURI:_currentNamespaceURI qulifiedName:_currentQName];
        
    }
//    else if (_delegate && [_delegate respondsToSelector:@selector(sxXMLParser:foundCharacters:)]) {
//        
//        [_delegate performSelector:@selector(sxXMLParser:foundCharacters:) withObject:self withObject:string];
//    }
}

// sent when an end tag is encountered. The various parameters are supplied as above.
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
    if (_delegate && [_delegate respondsToSelector:@selector(parser:didEndElement:namespaceURI:qualifiedName:)]) {
        [_delegate sxXMLParser:self didEndElement:elementName namespaceURI:namespaceURI qualifiedName:qName];
    }
    
    _currentElementName = nil;
    _currentNamespaceURI = nil;
    _currentQName = nil;
    _currentAttributeDict = nil;
}

// sent when the parser has completed parsing. If this is encountered, the parse was successful.
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
    if (_delegate && [_delegate respondsToSelector:@selector(sxXMLParserDidEndDocument:)]) {
        [_delegate performSelector:@selector(sxXMLParserDidEndDocument:) withObject:self];
    }
}

// ...and this reports a fatal error to the delegate. The parser will stop parsing.
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
{
    if (_delegate && [_delegate respondsToSelector:@selector(sxXMLParser:parseErrorOccurred:)]) {
        [_delegate performSelector:@selector(sxXMLParser:parseErrorOccurred:) withObject:self withObject:parseError];
    }
}

@end

④ 接下來,我們使用SXXMLParser嘗試進行XML解析

XMLViewController.m中,主要代碼如下

/**
 *  SXXMLParser
 *  基於NSXMLParser封裝
 */
- (void)parserXMLByNSXMLParserWithXMLPath:(NSString *)path
{
    NSURL *url = [NSURL fileURLWithPath:path];
    SXXMLParser *parser = [[SXXMLParser alloc] initWithContentsOfURL:url];
    parser.delegate = self;
    [parser parse];
}

#pragma mark - SXXMLParserDelegate
- (void)sxXMLParserDidStartDocument:(SXXMLParser *)parser
{
    self.productArray = [NSMutableArray arrayWithCapacity:2];
}

- (void)sxXMLParser:(SXXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary<NSString *,NSString *> *)attributeDict
{
    if ([elementName isEqualToString:@"product"]) {
        
        ProductModel *productModel = [[ProductModel alloc] init];
        productModel.productId     = [attributeDict objectForKey:@"id"];
        productModel.productName   = [attributeDict objectForKey:@"productName"];
        productModel.typeName      = [attributeDict objectForKey:@"typeName"];
        productModel.brand         = [attributeDict objectForKey:@"brand"];
        productModel.price         = [[attributeDict objectForKey:@"price"] doubleValue];
        
        [self.productArray addObject:productModel];
        
    } else if ([elementName isEqualToString:@"info"]) {
        
        ProductModel *productModel = self.productArray.lastObject;
        
        ProductInfoModel *productInfoModel = [[ProductInfoModel alloc] init];
        productInfoModel.infoId = [attributeDict objectForKey:@"id"];
        productInfoModel.infoLabel = [attributeDict objectForKey:@"label"];
        
        [productModel.productInfoArray addObject:productInfoModel];
        
        
    } else if ([elementName isEqualToString:@"items"]) {
        
        ProductModel *productModel = self.productArray.lastObject;
        ProductInfoModel *productInfoModel = productModel.productInfoArray.lastObject;
        
        ProductItemModel *productItemModel = [[ProductItemModel alloc] init];
        productItemModel.itemName          = [attributeDict objectForKey:@"name"];
        
        [productInfoModel.productItemArray addObject:productItemModel];
    }
}

- (void)sxXmlParser:(SXXMLParser *)parser foundCharacters:(NSString *)string element:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qulifiedName:(NSString *)qName
{
//    NSLog(@"\nelement      : %@ \nnamespaceURI : %@\nqName        : %@\nstring       : %@",elementName,namespaceURI,qName,string);
    
    ProductModel *productModel = self.productArray.lastObject;
    ProductInfoModel *productInfoModel = productModel.productInfoArray.lastObject;
    ProductItemModel *productItemModel = productInfoModel.productItemArray.lastObject;
    productItemModel.imagePath = string;
}

//- (void)sxXMLParser:(SXXMLParser *)parser foundCharacters:(NSString *)string
//{
//    NSLog(@"element : %@ - string : %@",parser.currentElementName,string);
//}

- (void)sxXMLParser:(SXXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
    
}

- (void)sxXMLParserDidEndDocument:(SXXMLParser *)parser
{
    NSLog(@"parser did end document!");
    for (ProductModel *productModel in self.productArray) {
    
        [_modelDescription appendFormat:@"\n%@",productModel.objectDictionary];
    }
    NSLog(@"%@",_modelDescription);

    _textView.text = _modelDescription;
}

- (void)sxXMLParser:(SXXMLParser *)parser parseErrorOccurred:(NSError *)parseError
{
    NSLog(@"%@",parseError.description);
}


###(2).使用TBXML解析,DOM模式,不支持XPath
①使用TBXML需要進行爲項目如下配置
A. 添加系統依賴庫

  • Foundation.framework
  • UIKit.framework
  • CoreGraphics.framework
  • libz.tbd

B.配置TBXML內存管理方式

  • 按住⌘+N調出Xcode創建文件菜單,選擇Other—>PCH File,創建PrefixHeader.pch文件
    pch

  • TBXML默認爲MRC內存管理,項目中定義ARC_ENABLED可以打開ARC開關,在pch文件裏添加如下代碼:

#import <Foundation/Foundation.h>
#define ARC_ENABLED
  • 將pch文件配置到項目中,Building Settings中搜索pch,並在Prefix Header下輸入剛纔創建的PrefixHeader.pch文件名.
    配置pch

②TBXML代碼講解

XMLViewController.m中,主要代碼如下

/**
 *  TBXML
 *  基於DOM的解析模式,但不支持XPath
 */
- (void)parserXMLByTBXMLWithPath:(NSString *)path
{
    path = [path componentsSeparatedByString:@"/"].lastObject;
    
    //  1.通過xml文件創建一個TBXML對象
    TBXML *tbxml = [[TBXML alloc] initWithXMLFile:path error:nil];
    //  2.查找文檔的根元素dataElement
    TBXMLElement *dataElement = tbxml.rootXMLElement;
    if (dataElement) {
        
        self.productArray = [NSMutableArray arrayWithCapacity:2];
        
        //  3.通過dataElement獲取其子元素productElment
        TBXMLElement *productElement = [TBXML childElementNamed:@"product" parentElement:dataElement];
        while (productElement != nil) {
            
            ProductModel *productModel = [[ProductModel alloc] init];
            //  4.獲取productElement的屬性值
            productModel.productId     = [TBXML valueOfAttributeNamed:@"id" forElement:productElement];
            productModel.productName   = [TBXML valueOfAttributeNamed:@"productName" forElement:productElement];
            productModel.typeName      = [TBXML valueOfAttributeNamed:@"typeName" forElement:productElement];
            productModel.brand         = [TBXML valueOfAttributeNamed:@"brand" forElement:productElement];
            productModel.price         = [[TBXML valueOfAttributeNamed:@"price" forElement:productElement] doubleValue];
            [self.productArray addObject:productModel];
            
            TBXMLElement *infoElement = [TBXML childElementNamed:@"info" parentElement:productElement];
            while (infoElement != nil) {
                
                ProductInfoModel *productInfoModel = [[ProductInfoModel alloc] init];
                productInfoModel.infoId            = [TBXML valueOfAttributeNamed:@"id" forElement:infoElement];
                productInfoModel.infoLabel         = [TBXML valueOfAttributeNamed:@"label" forElement:infoElement];
                [productModel.productInfoArray addObject:productInfoModel];
                
                TBXMLElement *itemsElement = [TBXML childElementNamed:@"items" parentElement:infoElement];
                while (itemsElement != nil) {
                    
                    ProductItemModel *productItemModel = [[ProductItemModel alloc] init];
                    productItemModel.itemName          = [TBXML valueOfAttributeNamed:@"name" forElement:itemsElement];
                    //  5.獲取itemsElement的文本內容
                    productItemModel.imagePath         = [TBXML textForElement:itemsElement];
                    [productInfoModel.productItemArray addObject:productItemModel];
                    
                    itemsElement = [TBXML nextSiblingNamed:@"items" searchFromElement:itemsElement];
                }
                
                infoElement = [TBXML nextSiblingNamed:@"info" searchFromElement:infoElement];
            }
            
            [_modelDescription appendFormat:@"\n%@",productModel.objectDictionary];
            
            //  6.獲取productElement的兄弟元素,及下一個productElement元素
            productElement = [TBXML nextSiblingNamed:@"product" searchFromElement:productElement];
        }
    }
    
    _textView.text = _modelDescription;

    NSLog(@"parser xml complete");
}

###(3).使用GDataXML解析,DOM模式,支持XPath

①項目配置如下

  • 添加系統依賴庫libxml2.tbd

  • Build Setting中搜索search,並在Header Search Paths中填入/usr/include/libxml2
    Header Search Paths

  • Build Setting中搜索Other Linker Flags,並在Other Linker Flags中填入-lxml2
    Other Linker Flags

②XPath

關於XPath的資料可以看這裏XPath教程-W3School

③GDataXML代碼詳解
XMLViewController.m中,主要代碼如下

- (void)parserXMLByGDataXMLNodeWithPath:(NSString *)path
{
    NSString *xmlString = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
    
    //  1.通過一個xmlString創建一個GDataXMLDocument對象
    GDataXMLDocument *doc = [[GDataXMLDocument alloc] initWithXMLString:xmlString options:0 error:nil];

    //  2.獲取根元素dataElement
    GDataXMLElement *dataElement = doc.rootElement;
    
    NSDictionary *namespacesDic =[NSDictionary dictionaryWithObjectsAndKeys:@"product.xml",@"xmlns", nil];
    if (dataElement != nil) {
        self.productArray = [NSMutableArray arrayWithCapacity:2];
        
        //  3.獲取子dataElement子元素productElement
        for (GDataXMLElement *productElement in dataElement.children) {
            
            ProductModel *productModel = [[ProductModel alloc] init];
            productModel.productId     = [productElement attributeForName:@"id"].stringValue;
            productModel.productName   = [productElement attributeForName:@"productName"].stringValue;
            productModel.typeName      = [productElement attributeForName:@"typeName"].stringValue;
            productModel.brand         = [productElement attributeForName:@"brand"].stringValue;
            productModel.price         = [productElement attributeForName:@"price"].stringValue.doubleValue;
            [self.productArray addObject:productModel];
            
            //  4.使用XPath方式獲取元素
            NSArray *infoElementArray = [productElement nodesForXPath:@"xmlns:info" namespaces:namespacesDic error:nil];
            for (GDataXMLElement *infoElement in infoElementArray) {
                
                ProductInfoModel *productInfoModel = [[ProductInfoModel alloc] init];
                productInfoModel.infoId            = [infoElement attributeForName:@"id"].stringValue;
                productInfoModel.infoLabel         = [infoElement attributeForName:@"label"].stringValue;
                [productModel.productInfoArray addObject:productInfoModel];
                
                for (GDataXMLElement *itemsElement in infoElement.children) {
                    
                    ProductItemModel *productItemModel = [[ProductItemModel alloc] init];
                    //  5.獲取itemsElement屬性
                    productItemModel.itemName          = [itemsElement attributeForName:@"name"].stringValue;
                    //  6.獲取itemsElement值
                    productItemModel.imagePath         = itemsElement.stringValue;
                    [productInfoModel.productItemArray addObject:productItemModel];
                }
            }
            [_modelDescription appendFormat:@"\n%@",productModel.objectDictionary];
        }
    }
    
    _textView.text = _modelDescription;
}

#三. XML生成

##1. 生成XML樣式
我們以下面的XML爲樣式,生成XML

pGenerate.xml

<?xml version="1.0" encoding="utf-8" ?>
<data xmlns="product.xml">	
	<product id="DB3" productName="聖象強化複合木地板" typeName="強化複合地板" brand="聖象" price="5">
		<info id="1" label="產品詳情">
			<item name="特性1">product/imgs/DB3/trait/1.jpg</item>
			<item name="特性1">product/imgs/DB3/trait/2.jpg</item>
			<item name="特性1">product/imgs/DB3/trait/3.jpg</item>
		</info>		
	</product>
</data>

##2. 使用GDataXML生成XML

###(1). 使用GDataXML說明
使用GDataXML生成XML時,需要注意的是,XML包含很多層級時,必須從內向外從最低層級開始添加元素.
如以上的XML,必須先創建最低層級item元素,然後創建product元素,並將item添加爲product的子元素.依次到data根元素.將根元素添加爲XML文檔的根元素

###(2). GDataXML生成XML代碼詳解
XMLViewController.m中,主要代碼如下

#pragma mark - xml generate
/**
 *  適應GDataXML生成XML文件
 *  注意,當XML包含很多層級時,必須由內向外先填元素,先創建最低級子元素,然後添加其父元素.
 */
- (void)generateXMLByGDataXML
{
    /** 我們簡單生成一個項目中和pGenerate.xml一樣的xml,格式如下
     
     <?xml version="1.0" encoding="utf-8" ?>
     <data xmlns="product.xml">
        <product id="DB3" productName="聖象強化複合木地板" typeName="強化複合地板" brand="聖象" price="5">
            <info id="1" label="產品詳情">
                <item name="特性1">product/imgs/DB3/trait/1.jpg</item>
            </info>
        </product>
     </data>
     
     */
    
    
    //  1.創建根元素
    GDataXMLElement *dataElement = [GDataXMLElement elementWithName:@"data"];
    GDataXMLNode *xmlnsNode = [GDataXMLNode attributeWithName:@"xmlns" stringValue:@"product.xml"];
    [dataElement addAttribute:xmlnsNode];
    
    //  2.創建product元素
    GDataXMLElement *productElement = [GDataXMLElement elementWithName:@"product"];
    //  3.設置product元素屬性
    GDataXMLNode *idNode = [GDataXMLNode attributeWithName:@"id" stringValue:@"DB3"];
    [productElement addAttribute:idNode];
    GDataXMLNode *productNameNode = [GDataXMLNode attributeWithName:@"productName" stringValue:@"聖象強化複合木地板"];
    [productElement addAttribute:productNameNode];
    GDataXMLNode *typeNameNode = [GDataXMLNode attributeWithName:@"typeName" stringValue:@"強化複合地板"];
    [productElement addAttribute:typeNameNode];
    GDataXMLNode *brandNode = [GDataXMLNode attributeWithName:@"brand" stringValue:@"聖象"];
    [productElement addAttribute:brandNode];
    GDataXMLNode *priceNode = [GDataXMLNode attributeWithName:@"price" stringValue:@"5"];
    [productElement addAttribute:priceNode];

    //  4.創建info元素
    GDataXMLElement *infoElement = [GDataXMLElement elementWithName:@"info"];
    GDataXMLNode *infoIdNode = [GDataXMLNode attributeWithName:@"id" stringValue:@"1"];
    [infoElement addAttribute:infoIdNode];
    GDataXMLNode *labelNode = [GDataXMLNode attributeWithName:@"label" stringValue:@"產品詳情"];
    [infoElement addAttribute:labelNode];
    
    //  5.創建item元素,並設置值
    GDataXMLElement *itemElement = [GDataXMLElement elementWithName:@"item" stringValue:@"product/imgs/DB3/trait/1.jpg"];
    GDataXMLNode *nameNode = [GDataXMLNode attributeWithName:@"name" stringValue:@"特性1"];
    [itemElement addAttribute:nameNode];
    
    
    //  6.將item添加爲info的子元素
    [infoElement addChild:itemElement];
    //  7.將info添加爲product的子元素
    [productElement addChild:infoElement];
    //  4.將product元素設置爲data的子元素
    [dataElement addChild:productElement];
    
    //  6.添加XML聲明,並設置根元素
    GDataXMLDocument *doc = [[GDataXMLDocument alloc] initWithRootElement:dataElement];
    [_modelDescription appendString:[[NSString alloc] initWithData:doc.XMLData encoding:NSUTF8StringEncoding]];
    
    
    _textView.text = _modelDescription;
    NSLog(@"%@",_modelDescription);
}

#四.XML其他參考資料及本人開發經驗

##1.簡單說幾句
本篇博客關於XML的說明,比較淺顯,Demo也做的很簡單,後續會陸續深入補充.先寫到這.

各位博友有什麼不清楚,或者有什麼關於文章/Demo改進的意見,歡迎留言交流!

##2.關於XML的其他參考資料

##3.本人做iOS XML時遇到的問題及解決

###(1). 特殊字符導致的XML格式錯誤

當XML中含有

&	且
<	小於
>   大於
"	雙引號
'   單引號

這些特殊符號時,如果不做轉義處理,會導致XML格式錯誤,解析出錯.
關於這類知識的詳解可以看上面的XML中必須進行轉義的字符.

###(2). 中文字符導致的一些信息獲取不到

有的時候需要在XML寫入圖片等資源路徑信息,去獲取資源,如果含有中文等字樣,XML格式沒有錯誤,但是會導致資源信息請求不到,儘量不要使用中文去命名資源.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章