12

我正在尝试使用补充 unicode 字符(例如 U+1D49C(

4

2 回答 2

6

由于我没有看到任何答案,而且其他人似乎也有同样的问题,所以我进一步调查了......

为了找到 bug 的来源,我使用了serializer来自 的源代码Xalan 2.7.1,它也用于Xerces.

org.apache.xml.serializer.dom3.LSSerializerImpl使用org.apache.xml.serializer.ToXMLStream, 扩展org.apache.xml.serializer.ToStream.

ToStream.characters(final char chars[], final int start, final int length)处理字符,并且不正确支持 unicode 字符(注意:(org.apache.xml.serializer.ToTextSream可以与 a 一起使用Transformer)在 characters 方法中做得更好,但它只处理纯文本并忽略所有标记;有人会认为 XML 文件是文本,但由于某种原因ToXMLStream没有扩展ToTextStream)。

org.apache.xalan.transformer.TransformerIdentityImpl也在使用org.apache.xml.serializer.ToXMLStream(由 返回org.apache.xml.serializer.SerializerFactory.getSerializer(Properties format)),所以它遇到了同样的错误。

ToStream用于org.apache.xml.serializer.CharInfo检查字符是否应替换为String,因此该错误也可以在那里修复而不是直接在ToStream. CharInfo正在使用org.apache.xml.serializer.XMLEntities.properties带有字符实体列表的属性文件 ,因此更改此文件也可能是修复错误的一种方法,尽管到目前为止它仅针对特殊 XML 字符(quotampltgt)设计。ToXMLStream使用与包中的属性文件不同的属性文件的唯一方法是org.apache.xml.serializer.XMLEntities.properties在类路径之前添加一个文件,这不是很干净......

使用默认的 JDK(1.6 和 1.7),TransformerFactory返回 a com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl,它使用com.sun.org.apache.xml.internal.serializer.ToXMLStream. In com.sun.org.apache.xml.internal.serializer.ToStream,characters()有时调用processDirty(), which 调用accumDefaultEscape(), 可以更好地处理 unicode 字符,但实际上它似乎不起作用(可能processDirty不调用 unicode 字符)...

com.sun.org.apache.xml.internal.serialize.DOMSerializerImpl正在使用com.sun.org.apache.xml.internal.serialize.XMLSerializer,它支持 unicode。奇怪的是,XMLSerializer 来自Xerces,但它并没有被Xerceswhenxalanxsltcare 在类路径中使用。这是因为org.apache.xerces.dom.CoreDOMImplementationImpl.createLSSerializerorg.apache.xml.serializer.dom3.LSSerializerImpl在可用时使用而不是org.apache.xerces.dom.DOMSerializerImpl. withserializer.jar在类路径上,org.apache.xml.serializer.dom3.LSSerializerImpl被使用。警告:xalan.jar并且xsltc.jar两者都serializer.jar在清单中引用,因此serializer.jar如果它位于同一目录中并且要么xalan.jar或要么位于类路径上,则最终会出现xsltc.jar在类路径上!如果只有xercesImpl.jarxml-apis.jar在类路径上,org.apache.xerces.dom.DOMSerializerImpl则用作LSSerializer,并且正确处理 unicode 字符。

结论和解决方法:该错误存在于 Apache 的org.apache.xml.serializer.ToStream类中(com.sun.org.apache.xml.internal.serializer.ToStream在 JDK 中重命名)。正确处理 unicode 字符的序列化程序是org.apache.xml.serialize.DOMSerializerImplcom.sun.org.apache.xml.internal.serialize.DOMSerializerImpl在 JDK 中重命名)。然而,Apache 更喜欢它ToStream何时DOMSerializerImpl可用,所以它可能在其他事情上表现得更好(或者它可能只是一个重组)。最重要的是,他们甚至DOMSerializerImplXerces 2.9.0. 因此,以下解决方法可能会产生副作用:

  • Xerces和 Apacheserializer在类路径上时,将“ (doc.getImplementation()).createLSSerializer()”替换为“ new org.apache.xerces.dom.DOMSerializerImpl()

  • 当 Apacheserializer在类路径上(例如因为xalan)而不是Xerces,尝试(doc.getImplementation()).createLSSerializer()用“new ”替换“ com.sun.org.apache.xml.internal.serialize.DOMSerializerImpl()”(回退是必要的,因为这个类将来可能会消失)

这两种解决方法在编译时会产生警告。

我没有解决方法XSLT transforms,但这超出了问题的范围。我想可以对另一个 DOM 文档进行转换并用于DOMSerializerImpl序列化。

其他一些解决方法,这对某些人来说可能是更好的解决方案:

  • Saxon与_Transformer

  • 使用带UTF-16编码的 XML 文档

于 2012-08-16T12:36:45.740 回答
2

这是一个对我有用的例子。代码是用在 Java 7 上运行的 Groovy 编写的,您可以轻松地将其转换为 Java,因为我在示例中使用了所有 Java API。如果您传入一个具有补充(平面 1)unicode 字符的 DOM 文档,您将得到一个正确序列化这些字符的字符串。例如,如果文档有一个 unicode Script L(参见http://www.fileformat.info/info/unicode/char/1d4c1/index.htm),它将在返回的字符串中序列化为&#x1d4c1而不是��(即Xalan Transformer 将获得什么)。

import org.w3c.dom.Document
...

def String writeToStringLS( Document doc ) {
  def domImpl = doc.getImplementation()
  def implLS = domImpl.getFeature("LS", "3.0")
  def lsOutput = implLS.createLSOutput()
  lsOutput.encoding = "UTF-8"
  def bo = new ByteArrayOutputStream()
  def out = new BufferedWriter( new OutputStreamWriter( bo, "UTF-8") )
  lsOutput.characterStream = out
  def lsWriter = implLS.createLSSerializer()
  def result = lsWriter.write(doc, lsOutput)
  return bo.toString()
}
于 2014-05-14T20:57:14.227 回答