2015-05-18 148 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/'