Arthur Felipe
Arthur Felipe

Reputation: 1436

How do I merge two XML files into one using Nokogiri?

I have two XML files and want to merge them, but the tags that are already there should not be changed:

XML 1:

<?xml version="1.0"?>
<formX xmlns="sdu:x">
  <identify>
    <mat>8</mat>
  </identify>
</formX>

XML 2:

<?xml version="1.0"?>
<formX xmlns="sdu:x">
  <identify>
    <mat>9999</mat>
    <name>John Smith</name>
  </identify>
</formX>

I want the result to be like this:

<?xml version="1.0"?>
<formX xmlns="sdu:x">
  <identify>
    <mat>8</mat>
    <name>John Smith</name>
  </identify>
</formX>

The previous tags should have the same values but with the addition of the new ones. Is that possible using Nokogiri? How?

At first I tried without Nokogiri using:

xml1 = Hash.from_xml('<?xml version="1.0"?>
<formX xmlns="sdu:x">
  <identify>
    <mat>8</mat>
  </identify>
</formX>')

But when I convert back to xml (xml1.to_xml) I get in wrong format:

"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<hash>\n  <formX>\n    <xmlns>sdu:x</xmlns>\n    <identify>\n      <mat>8</mat>\n    </identify>\n  </formX>\n</hash>\n"

Using Nokogiri, I came up with this solution, but really, it is so ugly and have a bug. If the xml2 doesnt have a element it will crash:

require 'nokogiri'

s = "<formAposentadoria xmlns=\"spu:aposentadoria\"><identificacao><matricula>8</matricula></identificacao></formAposentadoria>"
xml1 = Nokogiri::XML.parse s

s2 = "<formAposentadoria xmlns=\"spu:aposentadoria\"><identificacao><matricula>9</matricula><nome>John</nome></identificacao></formAposentadoria>"
xml2 = Nokogiri::XML.parse s2

def node_list elem, &proc
  return [] unless elem.class == Nokogiri::XML::Element
  str = proc.call(elem)
  [str] + elem.children.inject([]){|a,c| a+node_list(c,&proc)}.map{|e| "#{str}/#{e}"}
end

node_list(xml1.root){|e| e.name}.each do |x|
  caminho = '//xmlns:' + x.gsub('/', '/xmlns:')
  puts caminho
  if xml2.at_xpath( caminho ).children.children.count == 0
    xml2.at_xpath( caminho ).content = xml1.at_xpath( caminho ).content
  end
end

puts xml2.to_xml

Upvotes: 3

Views: 1542

Answers (1)

the Tin Man
the Tin Man

Reputation: 160581

Based on your samples and the desired output it appears you just want to replace the mat value in XML2 with the mat value from XML1.

require 'nokogiri'

xml1 = Nokogiri::XML('<?xml version="1.0"?>
<formX xmlns="sdu:x">
  <identify>
    <mat>8</mat>
  </identify>
</formX>')

xml2 = Nokogiri::XML('<?xml version="1.0"?>
<formX xmlns="sdu:x">
  <identify>
    <mat>9999</mat>
    <name>John Smith</name>
  </identify>
</formX>')

xml2.at('mat').content = xml1.at('mat').content

puts xml2.to_xml

Which outputs:

<?xml version="1.0"?>
<formX xmlns="sdu:x">
  <identify>
    <mat>8</mat>
    <name>John Smith</name>
  </identify>
</formX>

This isn't really a merge, it's a simple substitution. If there is more to the problem then your examples and desired output need to be modified to be more comprehensive.

Upvotes: 2

Related Questions