我正在尝试使用补充 unicode 字符(例如 U+1D49C(
2 回答
由于我没有看到任何答案,而且其他人似乎也有同样的问题,所以我进一步调查了......
为了找到 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 字符(quot、amp、lt、gt)设计。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,但它并没有被Xerceswhenxalan或xsltcare 在类路径中使用。这是因为org.apache.xerces.dom.CoreDOMImplementationImpl.createLSSerializer它org.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.jar和xml-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.DOMSerializerImpl(com.sun.org.apache.xml.internal.serialize.DOMSerializerImpl在 JDK 中重命名)。然而,Apache 更喜欢它ToStream何时DOMSerializerImpl可用,所以它可能在其他事情上表现得更好(或者它可能只是一个重组)。最重要的是,他们甚至DOMSerializerImpl在Xerces 2.9.0. 因此,以下解决方法可能会产生副作用:
当
Xerces和 Apacheserializer在类路径上时,将“(doc.getImplementation()).createLSSerializer()”替换为“new org.apache.xerces.dom.DOMSerializerImpl()”当 Apache
serializer在类路径上(例如因为xalan)而不是Xerces,尝试(doc.getImplementation()).createLSSerializer()用“new ”替换“com.sun.org.apache.xml.internal.serialize.DOMSerializerImpl()”(回退是必要的,因为这个类将来可能会消失)
这两种解决方法在编译时会产生警告。
我没有解决方法XSLT transforms,但这超出了问题的范围。我想可以对另一个 DOM 文档进行转换并用于DOMSerializerImpl序列化。
其他一些解决方法,这对某些人来说可能是更好的解决方案:
Saxon与_Transformer使用带
UTF-16编码的 XML 文档
这是一个对我有用的例子。代码是用在 Java 7 上运行的 Groovy 编写的,您可以轻松地将其转换为 Java,因为我在示例中使用了所有 Java API。如果您传入一个具有补充(平面 1)unicode 字符的 DOM 文档,您将得到一个正确序列化这些字符的字符串。例如,如果文档有一个 unicode Script L(参见http://www.fileformat.info/info/unicode/char/1d4c1/index.htm),它将在返回的字符串中序列化为𝓁而不是��(即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()
}