iOS开发指南-XML篇

本篇文章的所用的示例项目Demo可以在以下几个地方下载

  • GitHub下载(https://github.com/Story5/XMLDemo)
  • CSDN-CODE下载(https://code.csdn.net/Story51314/xmldemo/tree/master)

一. 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)子元素

在上面的示例中,
Province是Root的子元素.
所有的元素都要有开始标签和结束标签,且必须一致.

如果开始标签和结束标签之间没有内容,可以写成

<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中,支持SAX和DOM模式

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;

其上的方法调用的先后顺序如下

Created with Raphaël 2.1.0 XML解析器 XML解析器 NSXMLParserDelegate实现对象 NSXMLParserDelegate实现对象 1.parserDidStartDocument: 2.parser:didStartElement:namespaceURI:qualifiedName:attributes: 3.parser:foundCharacters: 4.parser:didEndElement:namespaceURI:qualifiedName: 5.parserDidEndDocument:

③ 为了程序的耦合性和简洁性,我们在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文件

  • TBXML默认为MRC内存管理,项目中定义ARC_ENABLED可以打开ARC开关,在pch文件里添加如下代码:

#import <Foundation/Foundation.h>
#define ARC_ENABLED
  • 将pch文件配置到项目中,Building Settings中搜索pch,并在Prefix Header下输入刚才创建的PrefixHeader.pch文件名.
    iOS开发指南-XML篇_第1张图片

②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
    iOS开发指南-XML篇_第2张图片

  • Build Setting中搜索Other Linker Flags,并在Other Linker Flags中填入-lxml2
    iOS开发指南-XML篇_第3张图片

②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的其他参考资料

  • XML教程-w3school
  • XML-百度百科
  • XML中必须进行转义的字符
  • XPath教程-W3School

3.本人做iOS XML时遇到的问题及解决

(1). 特殊字符导致的XML格式错误

当XML中含有

&   且
< 小于 >   大于
"   双引号
'   单引号

这些特殊符号时,如果不做转义处理,会导致XML格式错误,解析出错.
关于这类知识的详解可以看上面的XML中必须进行转义的字符.

(2). 中文字符导致的一些信息获取不到

有的时候需要在XML写入图片等资源路径信息,去获取资源,如果含有中文等字样,XML格式没有错误,但是会导致资源信息请求不到,尽量不要使用中文去命名资源.

你可能感兴趣的:(xml,sax,dom,GDataXML,NSXML)