带有表情符号字符的java xml解析器

时间:2015-08-07 00:23:30

标签: java xml parsing emoji

以下代码用于解析xml文件。我注意到没有正确解析表情符号字符。在示例中,输入末尾有一个表情符号(http://www.iemoji.com/view/emoji/693/people/revolving-hearts),输出中的字符加倍。这是一个已知的错误吗?

import java.io.File;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class XmlTest {

    public static void main(String[] args) {            
        DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
        domFactory.setValidating(false);
        File file = new File("c:\\temp\\emoji.xml");

        try {
            DocumentBuilder builder = domFactory.newDocumentBuilder();
            Document doc = builder.parse(file);

            NodeList nodes = doc.getElementsByTagName("entry");
            Node node = nodes.item(0);
            NamedNodeMap map = ((Element)node).getAttributes();

            for (int i=0; i<map.getLength(); i++) {
                Node n = map.item(i);
                System.out.println();
                System.out.println(n.getNodeValue());

                char[] chars = n.getNodeValue().toCharArray();

                for (int j=0; j<chars.length; j++) {
                    System.out.print(chars[j] + ", " + (int)chars[j] + "  ");                   
                }
            }

        } catch (Exception e) {e.printStackTrace(); }
    }
}

这里是输入emoji.xml:

<Attributes>
  <Map>
    <entry key="name" value="test"/>
  </Map>
</Attributes>

并输出:

name
n, 110  a, 97  m, 109  e, 101  
test
?, 55357  ?, 56478  t, 116  e, 101  s, 115  t, 116  ?, 55357  ?, 56478  ?, 55357  ?, 56478

2 个答案:

答案 0 :(得分:4)

我可以使用JDK 1.7重现该问题。

问题的原因似乎是JDK附带的XML解析器中的错误 (在这种情况下,它是Xerces,位于rt.jar中的包com.sun.org.apache.xerces.internal.*

表情符号字符不在Unicode BMP中,因此表示为两个字符(高和低代理)。当解析器遇到这些代理时,它会以特殊方式处理它们,并在转换为补充字符时检查它们是否是有效的XML字符。

错误代码位于以下代码部分的XMLScanner.scanAttributeValue

           } else if (c != -1 && XMLChar.isHighSurrogate(c)) {
                if (scanSurrogates(fStringBuffer3)) {
                    stringBuffer.append(fStringBuffer3);
                    if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) {
                        fStringBuffer2.append(fStringBuffer3);
                    }

将表情符号字符的两个字符解析为缓冲区变量fStringBuffer3,然后将其附加到缓冲区以获取属性值。现在的问题是fStringBuffer3未被清除。解析第二个表情符号字符时,它仍包含旧内容,因此字符会附加两次。

如果你尝试使用包含三个或更多表情符号的属性值,你会清楚地看到它们是如何积累的。

答案 1 :(得分:1)

一些更新:此问题已在Java 9的早期访问发行版本中修复(版本9-ea + 103-2016-01-27-183833.javare.4341.nc)。它仍然存在于Java 8的最新版本(build 1.8.0_72-b15)中。出于某种原因,Oracle因为针对此问题针对Java 6/7/8的服务请求而关闭了由于我的服务请求而打开的错误(因为不可重现)。我想让他们重新打开它。

这是针对openjdk打开的完全相同的问题,他们在openjdk 9中修复了它: https://bugs.openjdk.java.net/browse/JDK-8062362