REXML保留属性订购


3

我尝试使用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使用一些字典来保持属性...

我可以执行所需的顺序:键/值?

  0

您的第二个XML片段格式不正确。 22 2月. 092009-02-22 10:59:58

  0

除了可读性,为什么attributs命令很重要? 22 2月. 092009-02-22 11:03:54

  0

感谢您注意到xml代码中的错字! 22 2月. 092009-02-22 11:56:58

  0

我有一个类似的问题,使用REXML在现有的XML文件上做一些处理。该文件是我们的源代码的一部分,并检入源代码控制。虽然顺序在语义上不相关,但如果必须检查修改过的文件并重新排列属性,它会遮盖文件历史记录。 04 3月. 102010-03-04 22:16:19

6

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

根据XML规范here,请注意以下短语:“请注意,开始标记或空白元素标记中的属性规范顺序不重要”。

在回答你的具体问题,你是否可以强制执行某个命令,我不这么认为。我从来没有真的试过这样做(因为它是不必要的),但是REXML的人似乎不太可能浪费时间来实现这样的非功能:-)。由于键/值对以散列形式存储,因此它们的顺序可能是随机的(就像从键的字母顺序中可以看出的那样)。

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

既然你做一个简单的放,那么它可能使用非常格式化所以检查src/rexml/formatters/pretty.rbwrite_element代码而执行“node.attributes.each_attribute do |attr|”的开始 - 你会发现它是作为处理前分选名单一样简单元素。

您可能还希望建议开发商(见bug报告和增强请求here邮件列表或here),它们使这个在未来的版本的选择,但是,如果我是他们,我会简单地说这是没有必要的。

+3

当然,从机器的角度来看,订单并不重要。我只是为了可读性才需要它。其实我必须修改配置文件,并且尽可能地保留格式化。 22 2月. 092009-02-22 11:59:44

  0

查看可能的方法更新(基本上分叉为本地使用或试图说服开发人员添加此功能)。 22 2月. 092009-02-22 12:06:12


1

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

另外,请记住,通过REXML生成大量XML的速度非常慢。我有一个网站不得不读取和写入大量的XML;我发现对于阅读来说,REXML足够快,但对于写作,我不得不使用libxml。实际上,libxml的安装非常紧迫,ruby库对它来说太不成熟了,所以我最终使用erb来替换已经写好的XML文档的某些部分。

祝你好运!


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) 
  0

它就像一个魅力!感谢队友:) 20 6月. 132013-06-20 21:45:46


0

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

  0

验证是XML Schema的用途。就像XML标准一样,它不关心属性的排列顺序。 23 11月. 122012-11-23 22:56:01

  0

John的回答令人费解。 XML模式的验证功能仅限于文档的结构,对于验证文档的内容几乎没有什么作用。此外,建议我们不希望维护属性的顺序,因为这不是XML的功能,这意味着我们将任务的要求限制为仅限于技术可以即时提供的功能。我所做的工作解决了真正的问题,我使用最好的解决办法,在需要时进行调整。 30 7月. 132013-07-30 17:36:10

  0

为了保存属性序列,我编写了输出XML文档的代码。将XML生成为字符串数组的代码比大多数XML API更简洁,并且至少比创建瞬态DOM对象快4倍。 30 7月. 132013-07-30 17:53:03

  0

声明属性顺序无关紧要的是XML标准,而不是XML模式。 XML InfoSet没有属性顺序的概念。 30 7月. 132013-07-30 18:44:02


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