2015-05-18 118 views
1

我正在使用一些涉及多个名称空间的XML(具体来说是ResourceSync,它在Sitemap文档中嵌入了名称空间标记)。使用REXML编写名称空间XML

当我创建REXML元素,我可以设置一个全局命名空间:

foo = REXML::Element.new('foo') 
foo.add_namespace('http://foo.com/') 

puts foo # outputs <foo xmlns='http://foo.com/'/> 

,我可以创建一个命名空间的前缀:

foo.add_namespace('bar', 'http://bar.org/') 

puts foo # outputs <foo xmlns:bar='http://bar.org/' xmlns='http://foo.com/'/> 

但是,如果我再与添加其他元素与前缀名称相同的名称空间URI,但没有明确使用前缀 -

bar = REXML::Element.new('bar') 
bar.add_namespace('http://bar.org/') 
foo.add_element(bar) 

- REXML不够智能,无法注意前缀的存在并使用它。而不是预期的

<foo xmlns:bar='http://bar.org/' xmlns='http://foo.com/'> 
    <bar:bar/> 
</foo> 

我得到了不必要的冗长:

<foo xmlns:bar='http://bar.org/' xmlns='http://foo.com/'> 
    <bar xmlns='http://bar.org/'/> 
</foo> 

我可以变通的作法是完全无视空间URI,只是黑客前缀插入元素名称:

baz = REXML::Element.new('bar:baz') 
foo.add_element(baz) 

但是,在创建元素时,我唯一知道的就是名称空间URI - 我不知道要添加到哪个父元素或哪些名称空间前缀可能存在那里。 (名称空间前缀实际上不是逻辑文档模型的一部分,而名称空间URI是。)

有没有办法让REXML在输出时解析前缀,和/或直接后处理REXML文件使用前缀?

请注意,我不是在寻找例如一个Nokogiri解决方案,因为我正在使用一个库,xml-mapping在内部使用REXML(碰巧,它似乎也没有任何名称空间的概念,但我已经找到了解决这个问题的方法)。

回答

0

试试这个代码:

require 'rexml/document' 

foo = REXML::Element.new('foo') 
foo.add_namespace('http://foo.com/') 
foo.add_namespace('bar', 'http://bar.org/') 

bar = REXML::Element.new('bar') 
bar.add_namespace('http://bar.org/') 
foo.add_element(bar) 

def normalize_namespace!(elem) 
    if elem.attributes['xmlns'] 
    prefix = elem.namespaces.reject { |key, _| key == 'xmlns' }.key(elem.namespace) 
    elem.name = "#{prefix}:#{elem.name}" 
    elem.delete_namespace 
    end 
end 

foo.root.each_element_with_attribute('xmlns') { |e| normalize_namespace!(e) } 

puts foo 
# => <foo xmlns:bar='http://bar.org/' xmlns='http://foo.com/'><bar:bar/></foo> 

这里的解释:

  1. each_element_with_attribute遍历所有XML节点具有属性xmlns
  2. namespaces返回带有所有名称空间此节点,包括其祖先的散列,例如,用于bar这将是:{"xmlns"=>"http://foo.com/", "bar"=>"http://bar.org/"}
  3. namespace返回最合适的命名空间节点,通过检查它的属性和祖先。对于bar,它返回http://bar.org/
  4. name=访问都分配一个短和扩展名(最后将在渲染中使用,如果存在的话)
  5. 最后,delete_namespace将删除bar额外xmlns='http://bar.org/'