最近在项目中遇到了一个解析XML的问题,我们是用android自带的DOM解析器来解析XML的,但发现了一个android的问题,那就是在2.3的SDK上面,无法解析像<, >, 等字符串。

尽管我们从服务器端返回的数据中,应该是不能包含< >这样的字符,应该使用转义,但有时候,由于历史原因,导致服务器端不能作这样的修正,所以这样的问只能是在客户端来解决了。下面我就说一说我们是如何解决这种问的。

1,现象

我们的解析代码是:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 
DocumentBuilder builder = factory.newDocumentBuilder(); 
Document documnet = builder.parse(in);
Element root = documnet.getDocumentElement();

其中builder.parse(in)中的in是一个InputStream类型的输入流,例如有如下一段XML:

<?xml version="1.0" ?>
<data>
<success>1</success>
<error>
<code></code>
<message></message>
</error>
<result>
<history_info_list>
<row>
<purchase_info_id>dnrxmauxecj3z6e4</purchase_info_id>
<title_id>134051</title_id>
<title>まもって守護月天!再逢<Retrouvailles></title>
<volume_number>001</volume_number>
<author_name>桜野みねね</author_name>
<contents_name>まもって守護月天!再逢<Retrouvailles> 1巻</contents_name>
<date_open>2011-12-02</date_open>
<purchase_date>2012-02-06 18:39:48</purchase_date>
<image_url>/resources/c_media/images/thumb/262/134051_01_1_L.jpg</image_url>
<contents>
<story_number>1</story_number>
<contents_id>BT000013405100100101500014</contents_id>
<file_size>34168162</file_size>
<Within_Wifi>0</Within_Wifi>
</contents>
<text_to_speech_flg>0</text_to_speech_flg>
<restrict_num>-1</restrict_num>
<issue>3</issue>
<subscription>0</subscription>
<adult_flg>0</adult_flg>
</row>
</history_info_list>
</result>
</data>

其中有一个title结点,中间包含< >,但是XML中已经用了转义,所以应该是能正常解析出来的,但在SDK2.3(准确说来应该是3.0以下),它对这些转义字符作了特殊处理,它会把title中间文字当成四个文本结点,其内容分别是:

1, まもって守護月天!再逢

2, <

3, Retrouvailles

4, > 1巻

所以,这是不正确的,其实它应该就是一个节点,内容是[ まもって守護月天!再逢<Retrouvailles> 1巻 ]。不过在3.0的SDK,这种问题被修正了。

2,问题的原因

好,上面说的是现象,我们现在说一下造成这种现象的原因及解决办法。

翻看android源码发现:

android的XML解析实现用的是apache harmony代码,我想android的dalvik应该就是apache的harmonyxml parser,这个没有深究。

而实际上harmony的XML解析用的又是KXML,看来android就是一堆开源的代码叠加起来的。

下面仔细来看看:KXML的处理过程是这样的,对文本进行遍历,当发现<、/>、&等这些关键字符时,触发事件,有兴趣可以看看源码;

源代码在:\libcore\luni\src\main\java\org\apache\harmony\xml\parsers\DocumentBuilderImpl.java

113行: XmlPullParser parser = new KXmlParser();
265行:else if (token == XmlPullParser.TEXT)
node.appendChild(document.createTextNode(parser.getText()));
277行:else if (token == XmlPullParser.ENTITY_REF)
String entity = parser.getName(); if (entityResolver != null) {
// TODO Implement this...
} String replacement = resolveStandardEntity(entity);
if (replacement != null) {
node.appendChild(document.createTextNode(replacement));
} else {
node.appendChild(document.createEntityReference(entity));
}

从上面可以看到,处理带有&<&gt&;这些字符时,分成了几段文本节点。

3,解决方案

问题的原因我们已经知道了,怎么解决呢?

1,判断一下,如果子结点全是文本结点的话,把结点的所有文本字符串拼起来。

2,更改上面的处理方法,node.appendChild这行代码,当发现这个节点的第一个子节点是文本节点时,把当前字符加上去。

在项目中所采用的方法是第一种,因为这方法简单,实现如下:

/**
* This method is used to indicate the specified node's all sub nodes are text node or not.
*
* @param node The specified node.
*
* @return true if all sub nodes are text type, otherwise false.
*/
public static boolean areAllSubNodesTextType(Node node)
{
if (null != node)
{
int nodeCount = node.getChildNodes().getLength();
NodeList list = node.getChildNodes();
for (int i = 0; i < nodeCount; ++i)
{
short noteType = list.item(i).getNodeType();
if (Node.TEXT_NODE != noteType)
{
return false;
}
}
} return true;
} /**
* Get the node value. If the node's all sub nodes are text type, it will append
* all sub node's text as a whole text and return it.
*
* @param node The specified node.
*
* @return The value.
*/
private static String getNodeValue(Node node)
{
if (null == node)
{
return "";
} StringBuffer sb = new StringBuffer(); int nodeCount = node.getChildNodes().getLength();
NodeList list = node.getChildNodes();
for (int i = 0; i < nodeCount; ++i)
{
short noteType = list.item(i).getNodeType();
if (Node.TEXT_NODE == noteType)
{
sb.append(list.item(i).getNodeValue());
}
} return sb.toString();
}
}







Android XML解析器的问题的更多相关文章

  1. android XML解析器全解案例

    1.使用pull解析 package com.example.myxml; import java.io.InputStream; import java.util.ArrayList; import ...

  2. Android xml 解析

    XML 经常使用的三种解析方式: DOM: 所有载入到内存,生成一个树状结构,占用内存比較大. SAJ: 採用事件驱动,速度快,效率高,不支持回退. PULL:也是採用事件驱动,语法简洁. 步骤: 1 ...

  3. Android] Android XML解析学习——方式比较

     [Android] Android XML解析学习——方式比较 (ZT)  分类: 嵌入式 (From:http://blog.csdn.net/ichliebephone/article/deta ...

  4. Duilib源码分析(三)XML解析器—CMarkup

    上一节介绍了控件构造器CDialogBuilder,接下来将分析其XML解析器CMarkup: CMarkup:xml解析器,目前内置支持三种编码格式:UTF8.UNICODE.ASNI,默认为UTF ...

  5. tinyxml一个优秀的C++ XML解析器

    读取和设置xml配置文件是最常用的操作,试用了几个C++的XML解析器,个人感觉TinyXML是使用起来最舒服的,因为它的API接口和Java的十分类似,面向对象性很好. TinyXML是一个开源的解 ...

  6. TinyXML:一个优秀的C++ XML解析器

    //-------------------------------------------------------------------------------------------------- ...

  7. 转:TinyXM--优秀的C++ XML解析器

    读取和设置xml配置文件是最常用的操作,试用了几个C++的XML解析器,个人感觉TinyXML是使用起来最舒服的,因为它的API接口和Java的十分类似,面向对象性很好. TinyXML是一个开源的解 ...

  8. android XMl 解析神奇xstream 六: 把集合list 转化为 XML文档

    前言:对xstream不理解的请看: android XMl 解析神奇xstream 一: 解析android项目中 asset 文件夹 下的 aa.xml 文件 android XMl 解析神奇xs ...

  9. android XMl 解析神奇xstream 五: 把复杂对象转换成 xml ,并写入SD卡中的xml文件

    前言:对xstream不理解的请看: android XMl 解析神奇xstream 一: 解析android项目中 asset 文件夹 下的 aa.xml 文件 android XMl 解析神奇xs ...

随机推荐

  1. YIi配置debug工具、yii配置gii工具

    $config['bootstrap'][] = 'debug';$config['modules']['debug'] = [ 'class'=>'yii\debug\Module', 'al ...

  2. 远程读取URL 建议用curl代替file_get_contents

    初学php的朋友们,很容易翻一个错误,在写采集程序或者调用api接口总会有线考虑到使用file_get_contents函数来或许内容,程序的访问量不大倒是没什么影响,但是访问量提升了那非常的悲剧了, ...

  3. 高级UNIX环境编程10 信号

    信号是软件中断,提供一种处理异步事件的方法 <signal.h> sigaction()

  4. Mybatis 的Log4j日志输出问题 - 以及有关日志的所有问题

    使用Mybatis的时候,有些时候能输出(主要是指sql,参数,结果)日志.有些时候就不能. 无法输出日志的时候,无论怎么配置log4j,不管是properties的还是xml的,都不起作用. 有些时 ...

  5. GQ_百度百科

    GQ_百度百科 <GQ>杂志(原名<Gentlemen's Quarterly>,中文名<智族>)是一本男性月刊,内容著重于男性的时尚.风格.文化,也包括美食.电影 ...

  6. hdu 1232 畅通project

    题目: 链接:点击打开链接 算法: 赤裸裸的并查集. 代码: #include<iostream> #include<cstdio> #include<cstring&g ...

  7. android CircularSeekBar

    Android 中的 seekBar会被开发者经常用到,用的最多的空拍是控制音量.但是有时后为了更好的UI效果,横着的拖动条不能满足我们项目的需要,我们可能需要竖直的或者圆形的拖动条,那这两种样式的类 ...

  8. 警惕 MySql 更新 sql 的 WHERE 从句中的 IN() 子查询时出现的陷阱

    mer_stage 表有 216423 条记录,DDL: CREATE TABLE `mer_stage` ( `STAGE_ID` int(11) NOT NULL AUTO_INCREMENT, ...

  9. IPv6-only 的兼容性解决方案

    前几天Apple宣布 6月1日后所有应用必须支持IPv6-only网络 今天抽空看了下这方面的知识 首先解释下IPv6的是什么? 维基百科的定义如下:IPv6是Internet Protocol ve ...

  10. spark sql 基本用法

    一.通过结构化数据创建DataFrame: publicstaticvoid main(String[] args) {    SparkConf conf = new SparkConf() .se ...