android xml解析

本文详细介绍了XML的基本构成及其解析方法,包括DTD与Schema约束、命名空间的使用、推式与拉式解析器的区别,并深入探讨了Java ME环境中XmlPullParser的具体应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[b][size=medium]xml相关参考[/size][/b]
xml规范:[url]http://www.w3.org/TR/REC-xml/[/url]
Xerces-J sax解析器的一篇教程:[url]http://terpconnect.umd.edu/~zhangx/xml/html/xmlprog/xercessax/briefintro.html[/url]

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

[b]DOCTYPE声明[/b]
<!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文件,就是在文档标识符无法识别时备用的。

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

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

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

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

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

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

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


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

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

[b]命名空间:[/b]
命名空间的作用是用来区别有着相同名字,但却具有不同功能的东西。和编程语言的命名空间应该是差不多的功能。比如:同样的叫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>

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

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


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

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

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

[b]当我们不断的请求XmlPullParser事件时,会获取到的事件类型[/b]
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>,标签间的内容时

[b]xml的解析过程就是不断的请求事件,然后根据相应的事件做相应的处理[/b]
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。

[b]一般用途的解析[/b]
一般像微博这种,用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
}
}
}


[b]XmlPullParser其它api的使用[/b]
// 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[<> <div>]]><
</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();

// 文档结束
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值