REXML保留属性顺序

时间:2009-02-22 10:58:16

标签: xml ruby

我尝试使用REXML生成这样的XML

<root>
  <add key='foo' value='bar'/>
</root>

但我得到的是(注意键/值顺序)

<root>
  <add value='bar' key='foo'/>
</root>

代码:

require 'rexml/document'
include REXML

doc = Document.new
doc.add_element('root')
el = doc.root.add_element('add')
el.add_attribute('key', 'foo')
el.add_attribute('value', 'bar')
puts doc

如果我写下来并不重要:

el.add_attribute('key', 'foo')
el.add_attribute('value', 'bar')

el.add_attribute('value', 'bar')
el.add_attribute('key', 'foo')

结果是一样的。看起来REXML使用一些字典来保存属性......

我可以强制执行所需的订单:键/值吗?

5 个答案:

答案 0 :(得分:6)

在XML中,属性的顺序并不重要。如果你有一些XML处理代码,那么我会建议代码是错误的。

从XML规范here中,请注意短语:“请注意,start-tag或empty-element标记中的属性规范顺序并不重要。”

在回答您关于是否可以执行某项命令的具体问题时,我不相信。我从来没有尝试过这样做(因为这是不必要的)但REXML人员似乎不太可能浪费时间实现这样的非功能:-)。由于键/值对存储为散列,因此它们的顺序可能是随机的(就您可以从键的字母序列中看出来而言)。

当然,由于Ruby附带了REXML的源代码,您可以(如果绝望)用您自己的版本(REXML2?)替换或扩充包含的副本。

由于你正在做一个简单的放置,它可能正在使用漂亮的格式化器,所以检查write_elementsrc/rexml/formatters/pretty.rb代码的开头,它执行“node.attributes.each_attribute do |attr|” - 你可以发现它就像在处理元素之前对列表进行排序一样简单。

您可能还想向开发人员建议(有关邮件列表的here或有关错误报告和增强请求的here),他们会在将来的版本中将其作为选项,但是,如果我是他们,我只是说没必要。

答案 1 :(得分:6)

您可以尝试使用临时REXML::Formatter,而无需触及REXML来源。 A post on the ruby-talk ml建议使用此代码:

class OrderedAttributes < REXML::Formatters::Pretty
    def write_element(elm, out)
        att = elm.attributes

        class <<att
            alias _each_attribute each_attribute

            def each_attribute(&b)
                to_enum(:_each_attribute).sort_by {|x| x.name}.each(&b)
            end
        end

        super(elm, out)
    end
end

fmt = OrderedAttributes.new
fmt.write(doc, $stdout)

答案 2 :(得分:1)

如果您正在修改配置文件并且格式化很重要,那么通过REXML读取它可能更容易,但通过regexps进行修改。

另外,请记住,通过REXML生成大量XML非常慢。我有一个必须同时读写大量XML的网站;我发现,对于阅读来说,REXML足够快,但是对于写作,我必须使用libxml。实际上,libxml是一个如此安装的熊,它的ruby库如此不成熟,我最终使用erb来替换已经编写的XML文档的某些部分。

祝你好运!

答案 3 :(得分:1)

gioele优秀解决方案的简化版本:

如果我们对属性列表进行排序,那么输出是确定性的,这是避免生成的XML文档版本之间虚假更改的重要因素。

将这8行添加到您的脚本或应用程序中会使属性在任何地方排序,而无需进一步更改(例如,修改XML编写方式,或者找到每个位置元素被隐式转换为字符串并更改它使用格式化程序)。

# make REXML sort attributes by name so output is deterministic
module REXML
  class Attributes
    alias _each_attribute each_attribute
    def each_attribute(&b)
      to_enum(:_each_attribute).sort_by {|x| x.name}.each(&b)
    end
  end
end

答案 4 :(得分:0)

想要保留属性序列有许多正当理由。最重要的是验证修改XML的任何程序。维护属性序列时,可以使用简单的diff验证对文档的更改。保留将要显示给用户的信息序列是另一个。出于性能原因,XML标准采用了散列映射的路径,但我认为规范中缺少维护序列的特性是一个主要限制。

相关问题