使用 Java 将补充 unicode 字符序列化为 XML 文档
Posted
技术标签:
【中文标题】使用 Java 将补充 unicode 字符序列化为 XML 文档【英文标题】:Serializing supplementary unicode characters into XML documents with Java 【发布时间】:2012-08-10 17:51:22 【问题描述】:我正在尝试使用补充 unicode 字符(例如 U+1D49C(????,数学脚本大写 A))序列化 DOM 文档。创建具有这样一个字符的节点不是问题(我只是将节点值设置为等效的 UTF-16,“\uD835\uDC9C”)。然而,在序列化时,Xalan 和 XSLTC(使用 Transformer)和 Xerces(使用 LSSerializer)都会创建无效字符实体,例如“”而不是“【参考方案1】:
由于我没有看到任何答案,而且其他人似乎也有同样的问题,所以我进一步研究了......
为了找到bug的根源,我使用了Xalan 2.7.1
中的serializer
源代码,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
(可以与 Transformer
一起使用)在字符方法中做得更好,但它只处理纯文本和忽略所有标记;有人会认为 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
返回一个 com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl
,它使用 com.sun.org.apache.xml.internal.serializer.ToXMLStream
。在com.sun.org.apache.xml.internal.serializer.ToStream
中,characters()
有时调用processDirty()
,它调用accumDefaultEscape()
,可以更好地处理unicode 字符,但实际上它似乎不起作用(也许processDirty
不调用unicode 字符)。 ..
com.sun.org.apache.xml.internal.serialize.DOMSerializerImpl
正在使用com.sun.org.apache.xml.internal.serialize.XMLSerializer
,它支持 unicode。奇怪的是,XMLSerialize
r 来自Xerces
,但当xalan
或xsltc
在类路径上时,Xerces
并没有使用它。这是因为org.apache.xerces.dom.CoreDOMImplementationImpl.createLSSerializer
在可用时使用org.apache.xml.serializer.dom3.LSSerializerImpl
而不是org.apache.xerces.dom.DOMSerializerImpl
。在类路径上使用serializer.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
类(在 JDK 中重命名为 com.sun.org.apache.xml.internal.serializer.ToStream
)。正确处理 unicode 字符的序列化程序是 org.apache.xml.serialize.DOMSerializerImpl
(在 JDK 中重命名为 com.sun.org.apache.xml.internal.serialize.DOMSerializerImpl
)。然而,当它可用时,Apache 更喜欢ToStream
而不是DOMSerializerImpl
,所以它可能在其他事情上表现得更好(或者它可能只是一个重组)。最重要的是,他们甚至在 Xerces 2.9.0
中弃用了 DOMSerializerImpl
。因此,以下解决方法可能会产生副作用:
当 Xerces
和 Apache 的 serializer
在类路径中时,将“(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 文档
【讨论】:
我在 jira 上创建了一个错误报告:issues.apache.org/jira/browse/XALANJ-2560 Christopher Taylor 对 jira 发表了评论:“可能是 XALANJ-2419 的骗局,它附带了一个补丁。在 ***.com/questions/10511474/… 中也有引用”。 Xalan 最近有新的活动,因此有希望最终修复错误。 你让我进入了正确的方向 :) 我使用了一个库,它本身就使用了 xalan,其中不需要 xalan-serializer。虽然 xalan 附带对 xalan-serializer 的依赖项,但转储该依赖项会使我的库正常工作,万岁! 嗯,实际上我认为它是需要 xml-apis 的 xercesImpl 的依赖项,转储该依赖项使我的库工作。随便:万岁!注意:尽可能少的使用依赖【参考方案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()
【讨论】:
以上是关于使用 Java 将补充 unicode 字符序列化为 XML 文档的主要内容,如果未能解决你的问题,请参考以下文章
java.sql.SQLException: Illegal mix of collations (utf8_unicode_ci,IMPLICIT) ....错误