Search code examples
rubyxmlnamespacesnokogiriprefix

Nokogiri: controlling element prefix for new child elments


I have an xml document like this:

<?xml version="1.0" encoding="UTF-8"?>
<foo:root xmlns:foo="http://abc.com#" xmlns:bar="http://def.com" xmlns:ex="http://ex.com">
  <foo:element foo:attribute="attribute_value">
    <bar:otherElement foo:otherAttribute="otherAttributeValue"/>
  </foo:element>
</foo:root>

I need to add child elements to the element so that it looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<foo:root xmlns:foo="http://abc.com#" xmlns:bar="http://def.com" xmlns:ex="http://ex.com">
  <foo:element foo:attribute="attribute_value">
    <bar:otherElement foo:otherAttribute="otherAttributeValue"/>
    <bar:otherElement foo:otherAttribute="newAttributeValue"/>
    <ex:yetAnotherElement foo:otherAttribute="yetANewAttributeValue"/>
  </foo:element>
</foo:root>

I can add elements in the correct location using the following:

require 'rubygems'
require 'nokogiri'
doc = Nokogiri::XML::Document.parse(File.open("myfile.xml"))
el = doc.at_xpath('//foo:element')

newEl = Nokogiri::XML::Node.new("otherElement", doc)            
newEl["foo:otherAttribute"] = "newAttributeValue"
el.add_child(newEl)

newEl = Nokogiri::XML::Node.new("yetAnotherElement", doc)           
newEl["foo:otherAttribute"] = "yetANewAttributeValue"
el.add_child(newEl)

However the prefix of the new elements is always "foo":

<foo:root xmlns:foo="http://abc.com#" xmlns:bar="http://def.com" xmlns:ex="http://ex.com">
  <foo:element foo:attribute="attribute_value">
    <bar:otherElement foo:otherAttribute="otherAttributeValue" /> 
    <foo:otherElement foo:otherAttribute="newAttributeValue" /> 
    <foo:yetAnotherElement foo:otherAttribute="yetANewAttributeValue" /> 
  </foo:element>
</foo:root>

How can I set the prefix on the element name for these new child elements? Thanks, Eoghan


Solution

  • (removed bit about defining namespace, orthogonal to question and fixed in edit)

    just add a few lines to your code, and you get the result desired:

    require 'rubygems'
    require 'nokogiri'
    doc = Nokogiri::XML::Document.parse(File.open("myfile.xml"))
    el = doc.at_xpath('//foo:element')
    
    newEl = Nokogiri::XML::Node.new("otherElement", doc)            
    newEl["foo:otherAttribute"] = "newAttributeValue"
    # ADDITIONAL CODE
    newEl.namespace = doc.root.namespace_definitions.find{|ns| ns.prefix=="bar"}
    #
    el.add_child(newEl)
    
    newEl = Nokogiri::XML::Node.new("yetAnotherElement", doc)           
    newEl["foo:otherAttribute"] = "yetANewAttributeValue"
    # ADDITIONAL CODE
    newEl.namespace = doc.root.namespace_definitions.find{|ns| ns.prefix == "ex"}
    #
    el.add_child(newEl)
    

    and the result:

    <?xml version="1.0" encoding="UTF-8"?>
    <foo:root xmlns:abc="http://abc.com#" xmlns:def="http://def.com" xmlns:ex="http://ex.com" xmlns:foo="http://foo.com" xmlns:bar="http://bar.com">
      <foo:element foo:attribute="attribute_value">
        <bar:otherElement foo:otherAttribute="otherAttributeValue"/>
        <bar:otherElement foo:otherAttribute="newAttributeValue"/>
        <ex:yetAnotherElement foo:otherAttribute="yetANewAttributeValue"/>
      </foo:element>
    </foo:root>