`

android xml解析

阅读更多
xml相关参考
xml规范:http://www.w3.org/TR/REC-xml/
Xerces-J sax解析器的一篇教程:http://terpconnect.umd.edu/~zhangx/xml/html/xmlprog/xercessax/briefintro.html

xml的组成:
xml声明
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
version:xml的版本,一般都是1.0
encoding:xml的使用的编码,一般都是UTF-8
standalone:表示该文档是否依赖于其他的文档(即是否include了其他的文档)。include了就是no,没有时就是yes(默认是没有的)。

DOCTYPE声明
<!DOCTYPE yuan SYSTEM "DTDs/yuan.dtd">
文档声明引用的是文件时(这里就是DTDs/yuan.dtd)(相对路径、绝对路径或由url标识指定的文件)就使用SYSTEM关键字

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
文档声明引用的是文档标识符时(由公共组织定义的公认的标识符,如W3C定义的),就使用PUBLIC关键字。

这边还在文档标识符后加了url标识的dtd文件,就是在文档标识符无法识别时备用的。

processing instruction
<?xml-stylesheet href="XSL/yuan.html.xsl" type="text/xsl"?>
xml样式表指令。这里不做具体的介绍,只是知道下processing instruction在xml中是这么个东西。看api时也好理解一些。

xml元素(就是经常看到的标签)。
如在html中经常看到的就是:
<div></div> // 仅有名称的标签
<img /> // 自关闭标签
<a href=""></a> // 带有属性的标签

文本内容
就是在根标签内间的文本内容。(xml规范规定xml的内容必须在根标签内)
如:
<root>
text1
    <tag1>text2</tag1>
    <tag2>
        <tag3>text3</tag3>
    </tag2>
</root>

entity reference实体引用
在配置java环境变量时,就有一个类似的实体引用。比如我们会配置:JAVA_HOME:c:\java\jdk1.6.20,然后在path变量中加入:%JAVA_HOME%\bin;。这就是一个实体引用,就是把JAVA_HOME处的值替换过来。

xml中的实体引用的语法是:&[实体名];(windows的环境变量中的语法是%[实体名]%)。xml中也预定义了几个可被引用的实体名:就是lt(<), gt(>), amp(&), quot("), apos(')。

CDATA section
xml规范中规定,这部分的内容不需要(解析器)做任何的处理,就按它原来的内容显示。只要将这部分的内容需要放入<![CDATA[     内容处    ]]>中就可以了。
<![CDATA[ <div></div> &lt; &gt; &amp; ]]> 里面的内容不会被进行任何的处理。

注释
xml中使用<!-- -->来注释内容。
如:<!-- 这里面的内容都是注释 -->


xml中的相关术语:
dtd、schema:
两种文档约束语言,是用来写xml文档规则的。就是通过它们可以规定一个xml中可以使用哪些标签,哪些标签分别有哪些属性,哪个标签可以出现在哪个标签内部等一系列的规则。

well-formed:遵守xml规范的xml文档就是well-formed的。
valid:遵守dtd、schema约束的就是valid的。

命名空间:
命名空间的作用是用来区别有着相同名字,但却具有不同功能的东西。和编程语言的命名空间应该是差不多的功能。比如:同样的叫XmlParser,可能一个是来自java标准库的javax.xml.XmlParser(仅仅是举例);另一个是来自apache的(org.apache.XmlParser)。
xml规范规定,需要使用一个唯一的URI和其缩写来指定命名空间。同时,每个xml文档都具有一个默认的命名空间(即不指定时)

// 默认命名空间的一个元素
<yuanzhifei89
    xmlns:java="http://yuanzhifei89.iteye.com/javax/xml"
    xmlns:apache="http://yuanzhifei89.iteye.com/org/apache">
        // java命名空间的解析器去解析该xml
        <apache:XmlParser>d:\file.xml</apache:XmlParser>

        // apache命名空间的解析器去解析该xml
        <java:XmlParser>d:\file.xml</java:XmlParser>
</yuanzhifei89>

推式解析器和拉式解析器
推式解析器是一种主动类型的解析器,它在解析xml的过程中,会将解析到的东西主动的推送给我们。比如,在解析到<div>这种时,它就会发给我们一个startTag的事件;同时整个解析过程都是全自动的(即必须从xml开始到xml结束)。

拉式解析器与其相对,是一种被动的解析器,我们想要什么东西得向它去请求才行。比如,我们想要<div>这种时,必须不断的去next()来找到该事件;同时它的整个解析过程是被动的,我们想解析到哪就解析到哪。



专为JavaME等移动设备设计的解析api
该部分api主要在org.xmlpull.v1包下,主要有以下几个类或接口:
XmlPullParser:该接口定义了XMLPULL V1 API中需要的解析功能。
XmlSerializer:该接口定义了序列化xml的需要的方法。

XmlPullParserFactory:Xml解析器工厂类。一般都是用该工厂来创建解析器实例,这样在需要时切换为想要的解析器实现类。
XmlPullParserException:不需多解释,异常最好理解。在解析过程中遇到非正常情况就会抛出该异常。

使用XmlPullParser解析xml
XmlPullParser是一种拉式解析器,所有的事件必须我们自己去请求。

当我们不断的请求XmlPullParser事件时,会获取到的事件类型
DOCDECL
读到document type declaration时(就是<DOCTYPE),就报告该事件。

PROCESSING_INSTRUCTION
处理指令。看上面的xml组成中有介绍什么是处理指令。

CDSECT
读到CDATA Section就报告该事件

COMMENT
读到注释时就报告该事件

IGNORABLE_WHITESPACE
可忽略的空白。在没用dtd约束文档时,IGNORABLE_WHITESPACE只会出现在根元素外面;对于有dtd约束的文档,空白由dtd约束文档定义。(dtd约束文档就是在DOCTYPE中指定的那个文件,它规定了可以在xml出现什么标签、以及标签可以出现在哪等)

START_DOCUMENT
xml刚开始时,报告该事件

END_DOCUMENT
xml结束时,就报告该事件

START_TAG
读到开始标签时,就报告该事件。如:读到<div>这种时

END_TAG
读到结束标签时,就报告该事件。如:读到</div>这种时

TEXT
读到内容时,就报告该事件。如:读到<div>内容</div>,标签间的内容时

xml的解析过程就是不断的请求事件,然后根据相应的事件做相应的处理
XmlPullParser中提供的获取事件的方法:
next();
主要是用于返回比较高层的事件的。其中包括:START_TAG, TEXT, END_TAG, END_DOCUMENT

nextToken();
能够返回所有的事件。

对于一般的解析,只需要用到XmlPullParser的几个获取值的api就够了:
getName();
该api只有在当前的事件为START_TAG(返回标签名),END_TAG(返回标签名)和ENTITY_REF(返回实体引用名)。其它的事件都返回null。

getText();
该api只有在当前的事件为TEXT(返回文本内容),ENTITY_REF(返回实体引用引用的内容),CDSECT(返回其内部的内容),COMMENT(返回注释内容)。其它的事件一般都返回null。

nextText();
该api的正常执行是由条件的:要当前的事件为START_TAG,接下来两个事件为(TEXT), END_TAG。如果不满足就会抛异常(即:TEXT后再出现TEXT也是不行的)。如果当前事件为START_TAG,下一个为TEXT,则返回文本内容;如果下一个是END_TAG,则返回"",同时将事件置为END_TAG。

一般用途的解析
一般像微博这种,用xml作为数据交换时。返回的xml格式是很紧凑的(便于解析):
<root><data><create>8</create><fans>4</fans><home>9</home><mentions>0</mentions><private>0</private></data><errcode>0</errcode><msg>ok</msg><ret>0</ret></root>
<!-- 便于阅读的格式 -->
<root>
	<data>
		<create>8</create>
		<fans>4</fans>
		<home>9</home>
		<mentions>0</mentions>
		<private>0</private>
	</data>
	<errcode>0</errcode>
	<msg>ok</msg>
	<ret>0</ret>
</root>

XmlPullParserFactory parserFactory = XmlPullParserFactory.newInstance();
XmlPullParser parser = parserFactory.newPullParser();
// 因为xml仅仅是数据,没有xml声明,doctype这种,所以不需要做任何设置,直接解析即可
FileReader reader = new FileReader(xmlFile);
parser.setInput(reader);

// 该解析器是拉式解析器,所以只有靠我们自己去获取事件
// 当遇到END_DOCUMENT事件时,就可以结束事件的获取了,所以:
int eventType = 0;
String tagName = null;
while ((eventType = parser.next()) != XmlPullParser.END_DOCUMENT) {
	if (eventType == XmlPullParser.START_TAG) {
		tagName = parser.getName();
		
		// 正常情况下,已知的标签是START_TAG + TEXT + END_TAG的。
		// 意外情况(比如服务器返回的xml不完整),此时就应该抛异常,所以这里就按照正常的逻辑写代码好了
		if ("create".equals(tagName)) {
			// setCreateNum(parser.nextText());
		} else if ("fans".equals(tagName)) {
			// setFansNum(parser.nextText());
		} else if ("home".equals(tagName)) {
			// setHomeNum(parser.nextText());
		} else if ("mentions".equals(tagName)) {
			// setMentions(parser.nextText());
		} else if ("private".equals(tagName)) {
			// setPrivateNum(parser.nextText());
		} else {
			// unknown or new tag, just pass it
		}
	}
}


XmlPullParser其它api的使用
// property, feature相关方法
setFeature(String featureName, boolean state);
setProperty(String propertName, Object value);
getFeature(String featureName);
getProperty(String propertyName);

// xml读取方法
setInput(Reader in);
setInput(InputStream inputStream, String inputEncoding);

// 事件请求方法
next();
nextToken();
nextTag();
nextText();

// 其它相关方法
require(int eventType, String namespace, String name);
确定eventType是否和getEventType()相同,namespace是否和getNamespace()相同,name是否和getName()相同,有一个不同就抛异常。后两个可以为null(即忽略)。

getInputEncoding();
获取输入xml的encoding。如果能确定则返回编码,否则返回null。

// 事件类型都已int返回,要知道以文本方式显示的话可以这样
int eventType = parser.getEventType();
String evtTypeText = XmlPullParser.TYPES[eventType];

// 其它方法见示例:
要读取的xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<persons
	xmlns:yd="http://www.china-mobile.com"
	xmlns:lt="http://www.china-unicome.com">
	some_content
	<person index="1">
		<name>yuanzhifei89</name><age>100</age><married>false</married>
		<!-- 移动号码 --><yd:phone yd:id="amj08102">12345678</yd:phone>
		<!-- 联通号码 --><lt:phone lt:id="cmk35203">87654321</lt:phone>
		
		<![CDATA[&lt;&gt; <div>]]>&lt;
	</person>
</persons>

解析代码:从文档开始,一个事件一个事件解析到文档结束,基本涉及了所有api了。
因为有些api只有在特定的事件上才有返回值,所以有些调用只在部分标签处使用。
// 创建xml解析器
XmlPullParser parser = mFactory.newPullParser();
parser.setInput(new FileReader(xmlFile));

// 因为使用的xml涉及了命名空间,所以启用命名空间
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);

int eventType = 0;

// xml和person间的IGNOREABLE_WHITESPACE
eventType = parser.nextToken();
parser.isWhitespace(); // IGNOREABLE_WHITESPACE总是为whitespace

// 返回这两个前缀对应的命名空间uri,但此时还未解析到命名空间部分,所以均返回null
parser.getNamespace("lt");
parser.getNamespace("yd");

// 起始标签<persons>
eventType = parser.nextToken();
parser.getAttributeCount(); // 0,persones标签没有属性

// 命名空间已解析
parser.getNamespace("lt"); // http://www.china-unicome.com
parser.getNamespace("yd"); // http://www.china-mobile.comp

// <persons>和<person>间的text(空白也算在text内容中)
eventType = parser.nextToken();
parser.isWhitespace(); // false,只要含有非空白字符就不是whitespace

// 起始标签<person>
eventType = parser.nextToken();

// <person>和<name>间的text
eventType = parser.nextToken();
parser.isWhitespace(); // true

// 起始标签<name>
eventType = parser.nextToken();
parser.getLineNumber(); // 第7行
parser.getColumnNumber(); // 第9列

// name标签中的text:yuanzhifei89
eventType = parser.nextToken();

// 结束标签</name>
eventType = parser.nextToken();

// 起始标签<age>
eventType = parser.nextToken();
parser.getNamespace(); // "",age没有命名空间
parser.isEmptyElementTag(); // false,age并不是<age/>这种空标签
parser.getDepth(); // 3,<persons>一层,<person>二层,到它就是三层
parser.getNamespaceCount(parser.getDepth()); // 2,到三层为止解析到了2个命名空间
// age标签中的text:100
eventType = parser.nextToken();
// 结束标签</age>
eventType = parser.nextToken();

// 开始标签<married>
eventType = parser.nextToken();
// 标签married间的text:false
eventType = parser.nextToken();
// 结束标签</married>
eventType = parser.nextToken();

// </married>和<!-- 之间的text
eventType = parser.nextToken();

// COMMENT备注内容
eventType = parser.nextToken();

// 开始标签<yd:phone>
eventType = parser.nextToken();
parser.getAttributeCount(); // 1,它有一个属性
parser.getNamespace(); // http://www.china-mobile.com,它是该命名空间下的元素
parser.getPrefix(); // yd,命名空间前缀是yd
parser.getAttributeName(0); // id,第1个属性的名称
parser.getAttributeNamespace(0); // http://www.china-mobile.com,属性也在该命名空间下
parser.getAttributePrefix(0); // yd,所以前缀也一样
parser.getAttributeValue(0); // amj08102,属性值
parser.getDepth(); // 3,和<age>一样,<persons>一层,<person>二层,到它是三层
parser.getNamespaceCount(parser.getDepth()); // 2,到三层为止解析到了2个命名空间
// 标签<yd:phone>间的text:12345678
eventType = parser.nextToken();
// 结束标签</yd:phone>
eventType = parser.nextToken();

// </yd:phone>和<!--间的text
eventType = parser.nextToken();
parser.getName(); // phone,启用命名空间时返回phone;如果不启用会返回yd:phone

// COMMENT 备注内容
eventType = parser.nextToken();
parser.getText(); // 备注内容

// 开始标签<lt:phone>
eventType = parser.nextToken();
// 标签<lt:phone>间的内容
eventType = parser.nextToken();
// 结束标签</lt:phone>
eventType = parser.nextToken();

// </lt:phone>和<![CDATA[[]]>间的text
eventType = parser.nextToken();

// CDSECT:CDATA内容
eventType = parser.nextToken();
parser.getName(); // null
parser.getText(); // CDATA内容

// 实体引用lt
eventType = parser.nextToken();
parser.getName(); // 实体引用名lt
parser.getText(); // 所引用的内容<

// 实体引用lt和</person>间的text
eventType = parser.nextToken();

// 结束标签</person>
eventType = parser.nextToken();

// </person>和</persons>间的text
eventType = parser.nextToken();

// 结束标签</persons>
eventType = parser.nextToken();

// 文档结束
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics