最近在项目中遇到了一个解析XML的问题,我们是用android自带的DOM解析器来解析XML的,但发现了一个android的问题,那就是在2.3的SDK上面,无法解析像<, >, 等字符串。
一,问题现象
我们解析的代码是:
- 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>
- <result>
- <history_info_list>
- <row>
- <title>程序员 <特别版> 第1卷</title>
- <volume_number>001</volume_number>
- </row>
- </history_info_list>
- </result>
- </data>
1, 程序员
2, <
3, 特别版
4, > 第1卷
所以,这是不正确的,其实它应该就是一个节点,内容是[ 程序员<特别版> 第1卷 ]。不过在3.0的SDK,这种问题被修正了。
二,问题原因
好,上面说的是现象,我们现在说一下造成这种现象的原因及解决办法。看android源码发现: android的XML解析实现用的是apache harmony代码,我想android的dalvik应该就是apache的harmonyxml parser,这个没有深究。而实际上harmony的XML解析用的又是KXML,看来android就是一堆开源的代码叠加起来的。下面仔细来看看:XML的处理过程是这样的,对文本进行遍历,当发现<、/>、&等这些关键字符时,触发事件,有兴趣可以看看源码;
源代码在:\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));
- }
从上面可以看到,处理带有&<>&;这些字符时,分成了几段文本节点。
三,解决方案
问题的原因我们已经知道了,怎么解决呢?
1,判断一下,如果子结点全是文本结点的话,把结点的所有文本字符串拼起来。
2,更改上面的处理方法,node.appendChild这行代码,当发现这个节点的第一个子节点是文本节点时,把当前字符加上去。
在项目中所采用的方法是第一种,因为这方法简单,实现如下:
- 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;
- }
- 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();
- }