最近在项目中遇到了一个解析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. 使用wget -i下载多个文件

    使用wget -i下载多个文件 命令: wget -i filelist.txt 说明: 首先,保存一份下载链接文件 cat > filelist.txt url1 url2 url3 url4

  2. python学习之day9

    队列queue 队列是线程安全的,它保证多线程间的数据交互的一致性. 先进先出队列Queue import queue q = queue.Queue(maxsize=3) #maxsize为队列最大 ...

  3. jquery 动态增加的html元素,初始化设置在id或class上的事件无效

    一般情况,我们会在页面初始化完成后对class定义一些全局事件,举个栗子: $(document).ready(function(){ $(".class").on("m ...

  4. C语言之ASCII码

    ASCII码 ASCII码值在65~90之间,为大写字母.ASCII码值在97~122之间,为小写字母.ASCII码值在48~57之间,为数字.ASCII码值不在上述3个范围内,为特殊字符.

  5. linux 进程通信

    IPC: 管道,FIFO,信号,消息队列(system v/ posix),共享内存(system v/  posix),socket 同步机制: 互斥锁,条件变量,记录上锁, 信号量(system ...

  6. Web APIs 基于令牌TOKEN验证的实现

    Web APIs 基于令牌TOKEN验证的实现 概述: ASP.NET Web API 的好用使用过的都知道,没有复杂的配置文件,一个简单的ApiController加上需要的Action就能工作.但 ...

  7. FluentConsole是一个托管在github的C#开源组件

    FluentConsole是一个托管在github的C#开源组件 阅读目录 1.控制台能有啥滑头? 2.FluentConsole基本介绍 3.使用介绍 4.资源 从该系列的第一篇文章 .NET平台开 ...

  8. 常用的shell命令整理

    工作快一年了,shell命令也玩了一年了.还是有点积累的,下面是本人常用的. 1.pwd | xargs -i basename {}   获取当前所在目录的名称 2.ps -ef|grep -w   ...

  9. Android基调(十六)- Service:startService()、stopService()、bindService()、unbindService()加

    直行 第一 另外一种 第三种 总结 开门见山 开启服务有三种情况:假设直接使用服务,则没有必要进行绑定,可是假设要使用服务里面的方法.则要进行绑定. 具体的启动情况有下: ①调用startServic ...

  10. iOS UIView非常用方法及属性详解

    在调用视图的 Quartz  调用都可以正确地在视图中描画.   视图对象通过 bounds .和 center  属 性声明来跟踪自己的大小和位置.frame 属性包含一个矩形,即边框矩形,用于指定 ...