1

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.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
bicepjai
  • 1,615
  • 3
  • 17
  • 35
  • Why not show the code you used? Did you use regex, or a parser? If so, which one? – the Tin Man Jun 21 '11 at 00:34
  • 2
    That's not well-formed XML. Attribute names should not be quoted. Instead of `` it should be ``. – Mark Thomas Jun 21 '11 at 00:34
  • Also, you'll need to be specific about how you want to update the first file. Are you looking to append a node, add numbers, or what? It would be best if you provide an example of the output you are expecting. – Mark Thomas Jun 21 '11 at 00:38
  • I used REXML. I need to get an element that is more than 2 level deep. i searched almost all the apis available in http://www.germane-software.com/software/rexml_doc/ but i didnt find any api that will help me get the particular element so that i can add it. even in add element, the api is not clear with data about where the element is added – bicepjai Jun 21 '11 at 00:49
  • Part of the problem is the XML is not correct as @Mark Thomas pointed out. In a validating parser that can cause problems. Nokogiri is the HTML/XML parser I'd recommend over REXML. – the Tin Man Jun 21 '11 at 00:51
  • thanks the tin man. but occurring first is not my search criteria, the presence of attr id="true" and child is the searching criteria !! Also there might be 2 of those entries and then they both need to be updated. – bicepjai Jun 21 '11 at 01:07

2 Answers2

3

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".

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
  • This is a good answer. However the XPath @bicepjai wants is more like `//b[@id="true" and d/@do="me"]` and he'll need to iterate through all matches. – Mark Thomas Jun 21 '11 at 01:55
  • We don't know that. His question isn't clear and he says "an element", not "all elements". – the Tin Man Jun 21 '11 at 02:39
  • We do know that, because he responded as such to you, except in the wrong place (read his last comment to his own question). :) – Mark Thomas Jun 21 '11 at 17:14
0

the option //b[@id="true" and d/@do="me"]

with the above answer answers my question

bicepjai
  • 1,615
  • 3
  • 17
  • 35