【问题标题】:XML support for new UTF-8 like smileysXML 支持新的 UTF-8,如笑脸
【发布时间】:2017-01-13 13:53:57
【问题描述】:

我们有一个使用 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());

我是否必须指定一些编码或其他?

【问题讨论】:

  • 您可能别无选择,只能对包含表情符号的元素进行 base64 编码。甚至 ASCII 控制码作为 XML 文本也是非法的,例如

标签: java xml sax


【解决方案1】:

您的编码不正确。

如果您使用 XML 字符引用表示法 &amp;#NNNNN;,则 N 必须是 Unicode 代码点,而不是分割成代理对的 Unicode 代码点。例如,&amp;#x1f60e;。在您的示例中,&amp;#55357;&amp;#56846; 是不合法的,因为 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 中不使用代理对。尝试将 UTF-16 中的 Unicode 代码点编码为代理对,然后在 UTF-8 中独立编码该代理对的每一半,这是完全错误的。但我不知何故怀疑这就是你在做的事情。

【讨论】:

  • 我已经在底部添加了如何创建 XML,我没有看到任何明显的内容。
  • 您正在将 XML 创建为 StringWriter 中的字符串,它是 Java 字符序列(实际上是 UTF-16)。但是您向我们展示的 XML 声称它是 UTF-8 格式的。在某些时候,UTF-16 字符必须已转换为 UTF-8 八位字节,这可能就是问题所在。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-08-07
  • 2015-06-01
  • 2021-08-01
  • 2010-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多