java - XML 支持新的 UTF-8(如笑脸)

标签 java xml sax

我们有一个使用 XML 与服务器通信的移动客户端。当我们需要发送一些最新的 UTF-8 表情时,我遇到了一个问题,这些表情在新手机上很容易访问。例如:😉😯🙃😡😬😠。

现在,我的 Android 应用程序在编码和发送方面没有问题,但在服务器端,事情往往会更加爆炸。

如果我们尝试使用上面的任何表情发送消息,我们会得到一个巨大的堆栈跟踪,其中包含相关部分:

javax.xml.transform.TransformerException: org.xml.sax.SAXException: Invalid UTF-16 surrogate detected: d83d d83d ?
java.io.IOException: Invalid UTF-16 surrogate detected: d83d d83d ?
        at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(Unknown Source)
        at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(Unknown Source)

如果我们尝试解析它:

2017-01-13 14:00:22,717 - com.zylinc.core.gatekeeper.stripes.DoBean - WARN - Could not handle request
org.xml.sax.SAXParseException; lineNumber: 3; columnNumber: 93; Character reference "&#
        at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(Unknown Source)
        at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(Unknown Source)
        at com.zylinc.core.gatekeeper.stripes.DoBean.parseRequest(DoBean.java:127)
        at com.zylinc.core.gatekeeper.stripes.DoBean.execute(DoBean.java:56)
        at com.zylinc.core.gatekeeper.Dispatcher.onRequest(Dispatcher.java:107)
        at com.zylinc.core.gatekeeper.io.UntrustedSocketListener.handleRequest(UntrustedSocketListener.java:16)
        at com.zylinc.core.gatekeeper.io.SocketListener$MessageHandler.run(SocketListener.java:228)
        at java.lang.Thread.run(Unknown Source)

在这种情况下,XML 是:

<?xml version="1.0" encoding="UTF-8"?><action>
<set>
<absence requestid="0" from="2017 01 13 13 00 11" to="2017 01 13 22 59 11" subject="&#55357;&#56846;" user_id="CN=???????? ????????????,OU=TestUsers,OU=ZyUsers,DC=Zylinc,DC=com"/>
</set>
</action>

现在,这在输出 JSON 时似乎工作得很好,但是让客户端使用 JSON 并不是我们一朝一夕就能完成的事情。我猜它会中断,因为与 java 版本相比,使用的字符太新了,但最好确保新的表情永远不会中断消息传递。

解析 XML 的代码非常简单:

SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
XMLReader xmlReader = parser.getXMLReader();
xmlReader.setContentHandler(handler);
StringReader reader = new StringReader(xml);
xmlReader.parse(new InputSource(reader));

编辑:

创建 XML 的过程如下:

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
mDoc = builder.newDocument();
mRoot = mDoc.createElement("action");
mDoc.appendChild(mRoot);

TransformerFactory transFactory = TransformerFactory.newInstance();
Transformer trans = transFactory.newTransformer();
trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
trans.setOutputProperty(OutputKeys.INDENT, "yes");
trans.setOutputProperty(OutputKeys.VERSION, "1.1");

StringWriter sw = new StringWriter();
StreamResult result = new StreamResult(sw);
DOMSource source = new DOMSource(mDoc);
trans.transform(source, result);

return sw.toString();

添加文本很简单:

xml.setAttribute(SUBJECT, obj.getSubject());

我是否必须指定某种编码或其他编码?

最佳答案

您对这些内容的编码不正确。

如果您使用 XML 字符引用表示法 &#NNNNN;,则 N 必须是 Unicode 代码点,而不是分割成代理对的 Unicode 代码点。例如,😎。在您的示例中,您的 �� 是不合法的,因为 55357 和 56846 不是代码点,它们是代理对的两半。

如果您直接表示字符,我不确定您在做什么,但错误消息“检测到无效的 UTF-16 代理项:d83d d83d”非常清楚地表明您正在做什么错了。

您的问题标题(“UTF-8 就像笑脸”)表明您对 Unicode 和 UTF-8 感到困惑。 Unicode 将笑脸映射到整数代码点,例如第一个是十六进制 1f60e 或十进制 128526。UTF-8 是将 Unicode 编码为字节或八位字节流的一种可能方式,它可以将每个 Unicode 代码点编码为一到四个字节的序列。

UTF-16 是另一种编码,它将大多数 Unicode 代码点表示为 16 位,但 xffff 以上的代码点使用一对 16 位值(称为代理对)。 UTF-8 中不使用代理对。尝试将 Unicode 代码点编码为 UTF-16 作为代理对,然后将该代理对的每一半独立编码为 UTF-8,这是完全错误的。但我不知何故怀疑这就是你正在做的事情。

关于java - XML 支持新的 UTF-8(如笑脸),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41636186/

相关文章:

xml - 如何告诉 XML 解析器忽略丢失的实体

java - netty(或java NIO) channel 是先进先出的吗?

java - s :form tag action parameters being removed

java - 操作栏选项操作

android - 在布局中添加空格

java - 如何指定 Java Sax 解析器解析 XML 文件的一部分?

java - 带有可选嵌入式 Tomcat 容器的 Spring Boot 中的 JNDI

java - if 语句中可以有两个条件吗

javascript - 寻找更快或更好的方法来使用 jquery 选择 XML 节点的第 n 个子节点

java - 使用 javax.xml.validation.Validator 针对 XSD 进行 XML 验证