最近在项目中遇到了一个解析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();    }}









更多相关文章

  1. Android(安卓)ApiDemos示例解析(93):Text->LogTextBox
  2. [Android(安卓)基础] -- Android(安卓)8.1 开机流程分析(一)
  3. 深度剖析 Android音频系统解析与改进
  4. Android-Lifecycle超能解析-生命周期的那些事儿
  5. Android之AIDL使用解析
  6. Android(安卓)parse XML
  7. Android及Java使用google.zxing生成和解码(解析)二维码(QRCode)
  8. Android开发周报:Google收购Android十周年、Android最新技术解析
  9. Android(安卓)WIFI模块解析

随机推荐

  1. 解决 Android(安卓)Studio 创建项目时极
  2. android调用摄像头拍照,并且用imageView
  3. 资深Android研发全面总结:Flutter遇到的那
  4. 工程中导入sqlite -sqlite 基础教程(1)
  5. Android(安卓)首字母索引
  6. 浅谈Android应用的退出
  7. 解决ios 同层播放video 类似QQ登入背景图
  8. Android和iOS人才招聘出现拐点 低能人才
  9. Android(安卓)64bit系统中app以32bit运行
  10. Android研究院之应用开发线程池的经典使