Search code examples
rubyxmlrexml

How do I replace one node in an XML file with a node from another XML file?


I have two XML files. The first is:

<a>
  <b>
    <c1>1</c1>
  </b>
  <b>
    <c1>2</c1>
  </b>
  <b "id" = "true">
    <c1>3</c1>
    <d "do" ="me"></d>
  </b>
  <b id ="true">
    <c1>4</c1>
  </b>
</a>

And the second is:

<a>
  <b>
    <c1>5</c1>
  </b>
</a>

I want to update an element from first.xml:

<b "id" = "true">
  <c1>3</c1>
  <d "do" ="me"></d>
</b>

with an element from second.xml:

<b>
<c1>5</c1>
</b>

I tried to achieve that by deleting all the <b> nodes from first.xml and add the node <b> taken from second.xml file. I am able to delete all the nodes <b> but not able get an element from second.xml and add that to the first.xml.


Solution

  • After cleaning up the source XML, this seems to be what you're looking for:

    xml1 = <<EOT
    <a>
      <b>
        <c1>1</c1>
      </b>
      <b>
        <c1>2</c1>
      </b>
      <b id="true">
        <c1>3</c1>
        <d do="me"></d>
      </b>
      <b id="true">
        <c1>4</c1>
      </b>
    </a>
    EOT
    
    xml2 = <<EOT
    <a>
      <b>
        <c1>5</c1>
      </b>
    </a>
    EOT
    
    require 'nokogiri'
    
    doc1 = Nokogiri::XML(xml1)
    doc2 = Nokogiri::XML(xml2)
    
    doc1_b = doc1.at('//b[@id="true"]/c1/..')
    doc2_b = doc2.at('b')
    
    doc1_b.replace(doc2_b)
    
    puts doc1.to_html
    

    Which outputs:

    <a>
      <b>
        <c1>1</c1>
      </b>
      <b>
        <c1>2</c1>
      </b>
      <b>
        <c1>5</c1>
      </b>
      <b id="true">
        <c1>4</c1>
      </b>
    </a>
    

    doc1.at('//b[@id="true"]/c1/..')' means "find the first occurrence of a b tag with id="true" with a child c1 node".