Android XML解析器的問題

來源:互聯網
上載者:User

最近在項目中遇到了一個解析XML的問題,我們是用android內建的DOM解析器來解析XML的,但發現了一個android的問題,那就是在2.3的SDK上面,無法解析像<, >, 等字串。

儘管我們從伺服器端返回的資料中,應該是不能包含< >這樣的字元,應該使用轉義,但有時候,由於曆史原因,導致伺服器端不能作這樣的修正,所以這樣的問只能是在用戶端來解決了。下面我就說一說我們是如何解決這種問的。

1,現象
我們的解析代碼是:
[java] DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();  
DocumentBuilder builder = factory.newDocumentBuilder();  
Document documnet = builder.parse(in); 
Element root = documnet.getDocumentElement(); 

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document documnet = builder.parse(in);
Element root = documnet.getDocumentElement();其中builder.parse(in)中的in是一個InputStream類型的輸入資料流,例如有如下一段XML:


[html] <?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> 

<?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就是一堆開源的代碼疊加起來的。

 

[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)); 

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這行代碼,當發現這個節點的第一個子節點是文本節點時,把當前字元加上去。

在項目中所採用的方法是第一種,因為這方法簡單,實現如下:

 

[java] /**
     * 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(); 
    } 

/**
     * 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();
    }
}

 

 

 

 

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.