我正在尝试使用补充 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。奇怪的是,XMLSerialize
r 来自Xerces
,但它并没有被Xerces
whenxalan
或xsltc
are 在类路径中使用。这是因为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()
}