【问题标题】:Why is XmlParser converting my character hex code string to unicode?为什么 XmlParser 将我的字符十六进制代码字符串转换为 unicode?
【发布时间】:2013-09-26 22:46:33
【问题描述】:

在我的 Grails 应用程序中,我使用 Groovy 的 XmlParser 来解析 XML 文件。我的 XML 文件中的一个属性的值是一个字符串,它等于一个字符十六进制代码。我想将该字符串保存在我的数据库中:

Ñ

不幸的是,attribute 方法返回 Ñ 字符,而实际存储在数据库中的是c391。当该字段被读回时,我还会得到不想要的 Ñ 字符。

如何将十六进制代码作为字符串存储在我的数据库中,并确保它也作为十六进制代码被读回?

更新 #1:

这对我来说是个问题的原因是,一旦我将 XML 文件读入我的数据库,我必须能够完全按照它的原样重建它。另一个问题是有问题的字段并不总是字符十六进制代码。它可能只是一些任意字符串。

更新 #2:

我想字符在数据库中的存储方式并不重要,只要我能以扩展的十六进制代码格式将其写回即可。我正在使用 Groovy MarkupBuilder 从数据库中重建我的 XML 文件,但我不清楚为什么默认情况下不会发生这种情况。

更新 #3:

我在我的自定义 MySQL 方言中覆盖了 getTableTypeString,这似乎对某些事情有所帮助。至少现在我传递给 MySQL 的值是存储在数据库中的值。

class CustomMySQL5InnoDBDialect extends MySQL5InnoDBDialect {   
    @Override
    public String getTableTypeString() {
        return " ENGINE=InnoDB DEFAULT CHARSET=utf8"
    }
}

我还创建了自己的groovy.util.XmlParser 版本。我的版本几乎与groovy.util.XmlParser 完全相同,只是在startElement 方法中我更改了:

String value = list.getValue(i)

到这里:

def value = list.fAttributes.fAttributes[i].nonNormalizedValue
if(value ==~ /&#x([0-9A-F]+?);/) {
    value = list.fAttributes.fAttributes[i].nonNormalizedValue
}

这允许将十六进制代码元素的确切文本存储在数据库中。

现在有两个新问题,可能三个。

  1. 使用存储在数据库中的确切值重新创建文件。到目前为止,我一直在使用 MarkupBuilder,但这是对 & 符号进行额外编码,导致值 Ñ 被写为 Ñ 我可能可以通过放弃 MarkupBuilder 并构建我的 XML 字符串来解决这个问题手动,但我不想这样做。

  2. 使用 Saxon-HE 9.4 处理器对 XML 文件运行 XSLT 转换会导致某些十六进制代码值(例如 ÿ)更改为类似 ÿ 的值,而其他值则类似于 @ 987654338@ 保持不变。

  3. 我不确定这是否会成为问题,但是当我重新创建文件时,我希望它采用ANSI 编码,因为这是用于原始文件的编码.

【问题讨论】:

    标签: xml grails groovy xml-parsing xml-entities


    【解决方案1】:

    好的,给定xml:

    def xml = '''<root>
        <node woo="&#xD1;"/>
        <another attr="This is an N-Tilde - &#xD1;"/>
    </root>'''
    

    我们可以将该属性读入一个变量:

    def woo = new XmlParser().parseText( xml ).node[0].@woo
    

    打印出来给我们'Ñ'(字符值为209

    但这就是我所期望的......因为&amp;#xD1;&amp;#209; 相同,即correct encoding for N-tilde

    啊,问题也是如此“我怎样才能读取属性,并在没有任何实体解析的情况下保持原样”

    我不相信你可以(我所看到的都是来自网络搜索的否定答案)......你可以做的是:

    // Mask entities
    
    xml = xml.replaceAll( /&#x([0-9A-F]+?);/, '!!#x$1;' )
    
    def parser = new XmlParser().parseText( xml )
    
    println parser.node[0].@attr.replaceAll( /!!#x([0-9A-F]+?);/, '&#x$1;' )
    println parser.another[0].@attr.replaceAll( /!!#x([0-9A-F]+?);/, '&#x$1;' )
    

    但据我所知,没有一种方法可以关闭实体分辨率:-((手指交叉,我错了)

    【讨论】:

      【解决方案2】:

      我的 XML 文件中的一个属性的值是一个字符串,它等于一个字符十六进制代码

      不,不是。原XML中属性值的表示是十六进制字符引用,而属性的是字符Ñ。有一些方法可以配置一些 XML 解析器来避免在解析期间扩展命名的 entity 引用,但它们必须按照 XML 规范扩展数字字符引用。

      你还没有说为什么存储真正的字符值是一个问题。如果它与将值呈现给浏览器有关,那么可以在输出时使用.encodeAsHTML() 来处理。如果您需要将值保存到另一个 XML 文件,则使用 XML API 执行此操作,它将为您处理编码问题,用实体或字符引用替换字符以保持结果格式正确(在Ñ 的情况下,无论如何都不需要转义,除非您使用不寻常的字符集编写 XML)。

      在 Groovy 的 MarkupBuilder 的特定情况下,您可以暂时退出 XML 模式并使用 mkp.yieldUnescaped 将手动构建的标记直接写入输出流,这样您就可以在构建器通常不会打扰的地方输出字符引用。

      【讨论】:

      • 我更新了我的问题,说明为什么这对我来说是个问题。
      • @ubiquibacon 如果您的代码关心 Ñ、&amp;#xD1;&amp;#xd1; 等之间的区别,那么您不能使用 XML 工具来解析数据。 XML 解析器根本不会告诉您原始源代码中使用了哪种词法表示。
      • 我在我的问题中添加了一些新信息。看起来我不能使用任何基于 SAX 的解析器将字符十六进制代码读取为字符串,但也许你知道一种方法可以让 Groovy MarkupBuilder(或等效)以扩展的十六进制代码格式写出有问题的字符.
      • @ubiquibacon XML API 将转义任何需要转义的内容,而不是任何不需要转义的内容。如果您使用 UTF-8 编写 XML,则可以在不转义的情况下编写 Ñ。如果您将其编写为 US-ASCII,那么它将被转义为 &amp;#xD1;&amp;#209; 或其他等效字符引用。我再说一遍 - 如果您关心这种详细程度,那么您不是在处理 XML,也不能使用 XML 工具,而是必须自己将标记构造为字符串。
      • 我正在用 UTF-8 编写 XML,并且 Ñ 被写入文件。除了我要求能够生成与我作为输入的 XML 相同的 XML 之外,Saxon-HE 解析器会阻塞该字符。我就是这样发现这个问题的。
      猜你喜欢
      • 2014-12-22
      • 1970-01-01
      • 2017-05-16
      • 2018-01-31
      • 1970-01-01
      • 1970-01-01
      • 2013-02-07
      • 2018-01-26
      相关资源
      最近更新 更多