1

I am using Nokogiri to modify the content of an XML file:

<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
  <Default Extension="png" ContentType="image/png"/>
...
</Types>

I need add Default children to Types as follows:

<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
  <Default Extension="png" ContentType="image/png"/>
  <Default Extension="jpg" ContentType="image/jpeg"/>
...
</Types>

I tried:

child_node = Nokogiri::XML::Node.new "Default", @doc
@doc.xpath('//Types/Default').first.add_next_sibling(child_node)


 #but @doc.xpath('//Types/Default').first #=> nil

How can I add a child node to Types?

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Thang Le Sy
  • 107
  • 1
  • 4
  • 6

2 Answers2

2

It's very easy to modify nodes using Nokogiri:

require 'nokogiri'

doc = Nokogiri::XML(<<EOT)
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
</Types>
EOT

types = doc.at('Types')
types.children = '<Default Extension="png" ContentType="image/png"/><Default Extension="jpg" ContentType="image/jpeg"/>'

puts doc.to_xml

# >> <?xml version="1.0"?>
# >> <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
# >>   <Default Extension="png" ContentType="image/png"/>
# >>   <Default Extension="jpg" ContentType="image/jpeg"/>
# >> </Types>

Another way could be:

types = doc.at('Types')
[
  '<Default Extension="png" ContentType="image/png"/>',
  '<Default Extension="jpg" ContentType="image/jpeg"/>'
].each do |node|
  types.add_child(Nokogiri::XML::Node.new(node, doc))
end

puts doc.to_xml

# >> <?xml version="1.0"?>
# >> <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
# >> <<Default Extension="png" ContentType="image/png"/>/><<Default Extension="jpg" ContentType="image/jpeg"/>/></Types>

Note that Nokogiri is happy to coerce a tag in string form into a node and add it. That can make life a lot easier if you're dealing with boilerplate you want to insert.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
0

Using an XPath query against XML with a default namespace has been asked many times here in StackOverflow. In fact, I just posted an answer to a C# question on the same topic a couple hours ago.

I don't know Ruby in particular, but commonly, to select an element in a default namespace using XPath, you need to map a prefix to the default namespace URI and use that prefix in your XPath, something like:

child_node = Nokogiri::XML::Node.new "Default", @doc
result = @doc.xpath('/d:Types/d:Default', 'd' => 'http://schemas.openxmlformats.org/package/2006/content-types').first
Community
  • 1
  • 1
har07
  • 88,338
  • 12
  • 84
  • 137
  • 1
    Thanks a lot, it worked!
    result = @doc.xpath('/d:Types/d:Default', 'd' => 'http://schemas.openxmlformats.org/package/2006/content-types').first
    – Thang Le Sy Oct 16 '15 at 08:47
  • It's not necessary to thank us for edits. We do it because it improves the site. – the Tin Man Apr 26 '16 at 16:25