iOS 数据交换格式-XML

XML文档结构
// An highlighted block
<?xml version="1.0" encoding="UTF-8"?><!--(1)声明-->
<nodes><!--(2)根元素-->
	<note id="1"><!--(4)属性-->
		<name>名称</name><!--(3)子元素-->
		<remark>备注备注备注</remark><!--(3)子元素-->
	</note>
	<note id="2"><!--(4)属性-->
		<name>名称</name><!--(3)子元素-->
		<remark>备注备注备注</remark><!--(3)子元素-->
	</note>
</nodes><!--(2)根元素-->

XML文档的基本架构可以分为下面几个部分:
(1)声明,上面代码段(1)处就是XML文件的声明,它定义了XML文件的版本和使用的字符集,这里为1.0版,使用中文UTF-8字符。
(2)根元素,上面代码段(2)处是XML文件的根元素,是根元素的开始,是根元素的结束。根元素只有一个,开始标签和结束标签必须一致。
(3)子元素,上面代码段(3)处是XML文件的子元素,name、remark是根元素note的子元素。所有子元素都要有结束标签,开始标签和结束标签必须一致。如果标签中没有内容,可以简写成,这称为“空标签”。
(4)属性,上面代码段(4)处是属性,属性定义在开始标签中。id="1"是note元素的一个属性,id是属性名,1是属性值,属性值必须放在双引号或单引号中。一个元素不能有多个相同名字的属性。
(5)命名空间,用于为XML文档提供名字唯一的元素和属性。下面代码段(1)处。
(6)限定名,它是由命名空间引出的概念,定义了元素和属性的合法标识符。限定名通常在XML文档中用作特定元素或属性引用。下面代码段(2)处。

// An highlighted block
<?xml version="1.0" encoding="UTF-8"?>
<soap:note1 xmlns:xsi="https://www.a.com/XMLSchema-instance"
	xmlns:xsd="https://www.b.com/XMLSchema"
	xmlns:soap="https://c.com/envelope/"><!--(1)命名空间-->
	<soap:Body><!--(2)限定名-->
		
	</soap:Body>
</soap:note1>

XML文档解析

解析XML两种流行模式,SAX和DOM。
SAX模式,优点是解析速度快,弊端是只能读不能写入XML文档,iOS重点推荐使用SAX模式解析。
DOM模式,XML解析器在加载XML文件后,将XML文件的元素视为一个树状结构的节点,一次性的读入内存。如果文档比较大,解析速度会变慢。有点是能够修改文档。

iOS SDK提供了两个XML框架,如下:
(1)NSXML,它是基于ObjectiveC的SAX解析框架,是默认的XML解析框架,不支持DOM模式。
(2)libxml2,他是基于C语言的XML解析器,支持SAX和DOM模式。

此外,还有很多第三方框架可以使用,如下:
(1)TBXML,轻量级DOM解析库,不支持XML文档验证和XPath,只能读取XML文档,不能写入XML文档,但解析速度很快。
(2)TouchXML,基于DOM模式的解析库,与TBXML类似,只能读取XML文档,不能写入。
(3)KissXML,基于DOM模式的解析库,基于TouchXML,不同是可以写入XML文档。
(4)TinyXML,基于C++语言的DOM模式解析库,可以读写XML文档,不支持XPath。
(5)GDataXML,基于DOM模式的解析库,可以读写XML文档,支持XPath查询。

NSXML解析

在SAX解析器从上到下遍历文档的过程中,会遇到开始标签、结束标签、文档开始,文档结束和字符串时,就会触发下面几个常用方法:
**(1)parserDidStartDocument:**在文档开始的时候触发。
**(2)parser:didStartElement:namespaceURI:qualifiedName:attributes:**遇到第一个开始标签时触发,其中namespaceURI部分是命名空间,qualifiedName是限定名,attributes是字典类型的属性集合。
**(3)parser:foundCharacters:**遇到字符串时触发。
**(4)parser:didEndElement:namespaceURI:qualifiedName:**遇到结束标签时触发。
**(5)parserDidEndDocument:**在文档结束时触发。

NSXMLParser时解析类,有三个构造函数:
**(1)initWithContentsOfURL:**可以使用URL对象创建解析对象
**(2)initWithData:**可以使用NSData(Swift是Data)创建解析对象,Swift表示为init(data:Data)。
**(3)initWithStream:**可以使用IO流对象创建解析对象,Swift中表示为init(stream:)。
解析对象创建好后,需要制定委托属性delegate为self,然后发送parse消息,开始解析文档。
例:解析方法
Swift代码

// An highlighted block
import Foundation

class NotesXMLParser:NSObject,NSXMLParserDelegate{
	//解析处的数据内部是字典类型
	private var listData:NSMutableArray!
	//当前标签的名字
	private var currentTagName:String!

	//开始解析
	func start(){
		let path=Bundle.main.path(forResource:"Notes",ofType:"xml")!
		let url=URL(fileURLWithPath:path)
		//开始解析XML
		let parser=XMLParser(contentsOf:url)!
		parse.delegate=self
		parse.parse()
	}
	//解析开始,只触发一次
	//可以在这个方法中初始化解析过程中用到的一些成员变量。
	func parserDidStartDocument(parser:NSXMLParser!){
		self.listData=NSMutableArray()
	}
	//此方法一般在调试阶段用,实际发布时意义不大。更不要对用户使用UIAlertView提示,用户会被这些专业的错误吓坏。
	func parser(parser:NSXMLParser,parseErrorOccurred parseError:NSError){
		print(parserError)
	}
	//遇到第一个开始标签时触发
	func parser(_ parser:XMLParser,didStartElement elementName:String,namespaceURI:String?,qualifiedName qName:String?,attributes attributeDict:[String:String]){
		//elementName:正在解析的元素名字
		self.currentTagName=elementName
		//如果名字是"Note"去除它的id。属性是从attributeDict中传递过来的,它是一个字典类型,键的名字是属性名字,值是属性的值。
		if self.currentTagName="Note"{
			let id=attributeDict["id"]
			let dict=NSMutableDictionary()
			dict["id"]=id
			self.listData.add(dict)
		}
	}
	//遇到字符串时触发
	func parser(_ parser:XMLParser,foundCharacters string:String){
		//替换回车符合空格
		let s1=string.trimmingCharacters(in:CharacterSet.whitespacesAndNewLines)
		if s1=""{
			return
		}
		let dict=self.listData.lastObject as! NSMutableDictionary
		if(self.currentTagName=="CDate"){
			dict["CDate"]=string
		}
		if(self.currentTagName=="Content"){
			dict["Content"]=string
		}
	}
	//遇到结束标签时触发
	func parser(_ parser:XMLParser,didEndElement elementName:String,namespaceURI:String?,qualifiedName qName:String?){
		self.currentTagName=nil
	}
	//遇到结束标签时触发
	func parserDidEndDocument(_ parser:XMLParser){
		print("NSXML解析完成")
		
	}
}

ObjectiveC代码

// An highlighted block
//NotesXMLParser.h文件代码
@interface NotesXMLParser:NSObject<NSXMLParserDelegate>
	//解析处的数据内部是字典类型
	@property(strong,nonatomic)NSMutableArray*listData;
	//当前标签的名字
	@property(strong,nonatomic)NSString*currentTagName;
	//开始解析
	-(void)start;
@end

//NotesXMLParser.m文件代码
//开始解析
-(void)start{
	NSString *path=[[NSbundle mainBundle] pathForResource:@"Notes" ofType:@"xml"];
	NSURL *url=[NSURL fileURLWithPath:path];
	//开始解析
	NSXMLParser *parser=[[NSXMLParser alloc] initWithContentsOfURL:url];
	parser.delegate=self;
	[parser parse];
}
//解析开始,只触发一次
//可以在这个方法中初始化解析过程中用到的一些成员变量。
-(void)parserDidStartDocument:(NSXMLParser *)parser{
	self.listData=[[NSMutableArray alloc] init];
}
//此方法一般在调试阶段用,实际发布时意义不大。更不要对用户使用UIAlertView提示,用户会被这些专业的错误吓坏。
-(void)parser:(NSXMLParser*)parser parserErrorOccurred:(NSError*)parseError{
	NSLog(@"%@",parseError);
}
//遇到第一个开始标签时触发
-(void)parser:(NSXMLParser*)parser didStrtElement:(NSString*)elementName namespaceURI:(NSString*)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary*)attributedDict{
	self.currentTagName=elementName;
	if([self.currentTagName isEqualToString:@"Note"]){
		NSString *identifier=[attributeDict objectForKey:@"id"];
		NSMutableDictionary *dict=[[NSMutableDictionary alloc] init];
		[dict setObject:identifier forKey:@"id"];
		[self.listData addObject:dict];
	}
}
//遇到字符串时触发
-(void)parser:(NSXMLParser*)parser foundCharacters:(NSString*)string{
	//替换回车符合空格
	string=[string stringByTrimmingCharactersInSet:[NSCharacterSet WhitespaceAndNewlineCharacterSet]];
	if([string isEqualToString:@""]){
		return;
	}
	NSMutableDictionary *dict=[self.listData lastObject];
	if([self.currentTagName isEqualToString:@"CDate"] && dict){
		[dict setObject:string forKey:@"CDate"];
	}
	if([self.currentTagName isEqualToString:@"Content"] && dict){
		[dict setObject:string forKey:@"Content"];
	}
}
//遇到结束标签时触发
-(void)parser:(NSXMLParser*)parser didEndElement:(NSString*)elementName namespaceURI:(NSString*)namespaceURI qualifiedName:(NSString*)qName{
	self.currentTagName=nil;
}
//遇到文档结束时触发
-(void)parserDidEndDocument:(NSXMLParser*)parser{
	NSLog(@"解析完成");
}
TBXML解析

使用前先下载TBXML。下载后将TBXML-Headers和TBXML-Code文件夹添加到工程中,然后在工程中添加TBXML所依赖的Framework和库,如下:
CoreGraphics.framework
libz.tbd
然后做一些配置,Objective-C与Swift有区别。
ObjectiveC配置
先在预编译头文件中添加如下代码:
#import <Foundation/Foundation.h>
#define ARC_ENABLED
配置预编译头文件位置,Build Settings->Apple LLVM8.0-Language->Prefix Header。
Swift配置
工程预编译头文件对Swift是不管用的需要在每个头文件中添加如下内容:
#import <Foundation/Foundation.h>
#define ARC_ENABLED
因为要在Swift中调用ObjectiveC,所以要在桥接头文件中引入头文件TBXML.h。
配置桥接头文件位置:Build Settings->Swift Compiler-Code General->Objective-C Bridging Header。

TBXML构造函数3个,如下:
(1)initWithXMLString:error:,通过XML字符串构造TBXML对象。
(2)initWithXMLString:,通过XML字符串构造TBXML对象。
(3)initWithXMLData:error:,通过NSData数据构造TBXML对象,这个方法非常适用于在网络通信下解析。
例:
Swift代码

// An highlighted block
class NotesTBXMLParser:NSObject{
	//解析处的数据内部是字典类型
	private var listData:NSMutableArray!
	//开始解析
	func start(){
		self.listData=NSMutableArray()
		//构造函数创建TBXML对象
		let tbxml=TBXML(xmlFile:"Notes.xml")
		//获得文档的根元素对象,由于不支持XPath,解析文档需要从根元素开始。
		let root=tbxml?.rootXMLElement
		if root=tbxml?.rootXMLElement
		if root !=nil{
			//查找root元素下面的Note元素,childElementNamed:方法值返回第一个Note元素
			var noteElement=TBXML.childElementNamed("Note",parentElement:root)
			while noteElement!=nil{
				let dict=NSMutableDictionary()
				let dateElement=TNXML.childElementNamed("CDate",parentElement:noteElement)
				if dateElement!=nil{
					//获取元素的文本内容
					let date=TBXML.text(for:dateElement)
					dict["CDate"]=date
				}
				let contentElement=TBXML.childElementNamed("Content",parentElement:noteElement)
				if contentElement !=nil{
					let content=TBXML.text(for:contentElement)
					dict["Content"]=content
				}
				//获取属性值
				let identifier=TBXML.value(ofAttributeNamed:"id",for:noteElement)
				dict["id"]=identifier
				self.listData.add(dict)
				//获得同层下一个Note元素,Sibling:兄弟的意思
				noteElement=TBXML.nextSiblingNamed("Note",searchFrom:noteElement)
			}
		}
		print("TBXML解析完成")
		
	}
}

ObjectiveC代码

// An highlighted block
@implementation NotesTBXMLParser
//开始解析
-(void)start{
	self.listData=[[NSMutableArray alloc] init];
	//构造函数创建TBXML对象
	TBXML*tbxml=[[TBXML alloc] initWithXMLFile:@"Notes.xml" error:nil];
	//获得文档的根元素对象,由于不支持XPath,解析文档需要从根元素开始。
	TBXMLElement *root=tbxm.rootXMLElement;
	//如果root元素有效
	if(root){
		//查找root元素下面的Note元素,childElementNamed:方法值返回第一个Note元素
		TBXMLElement *noteElement=[TBXML childElementNamed:@"Note" parentElement:root];
		while(noteElement!=nil){
			NSMutableDictionary *dict=[NSMutableDictionary new];
			TBXMLElement *dateElement=[TBXML childElementNamed:@"CDate" parentElement:noteElement];
			if(dateElement!=nil){
				//获取元素的文本内容
				NSString *date=[TBXML textForElement:dateElement];
				dict[@"CDate"]=date;
			}
			TBXMLElement *contentElement=[TBXML childElementName:@"Content" parentElement:noteElement];
			if(contentElement!=nil){
				NSString *content=[TBXML textForElement:contentElement];
				dict[@"Content"]=content;
			}
			//获取属性值,获得id属性
			NSString *identifier=[TBXML valueOfAttributeName:@"id" forElement:noteElement error:nil];
			dict[@"id"]=identifier;
			//获得同层下一个Note元素,Sibling:兄弟的意思
			noteElement=[TBXML nextSiblingNamed:@"Note" searchFromElement:noteElement];
		}
	}
	NSLog(@"TBXML解析完成");
}
@end

参考资料
《IOS开发指南 从HELLO WORLD到APP STORE上架 第5版》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值