2

I have an xml file with the following structure:

<main_tag>
   <first>
     <tag1>val1</tag1>
     <conf>
       <tag2>val2</tag2>
       <tag3>val3</tag3>
       <tag4>val4</tag4>
     </conf>
   </first>
   <second>
     <tag1>val2</tag1>
     <conf>
       <tag2>val6</tag2>
       <tag3>val7</tag3>
       <tag4>val8</tag4>
     </conf>
   </second>
</main_tag>

I have to change the value of tag2. Possible values are stored in a dict:

{tag2values:[newvalue1, newvalue2]}

If value of tag1 is val1, then we change tag2 value to newvalue1. If tag1 value is val2, then we change tag2 value to newvalue2.

So the question is: is there a way to find an element in lxml matching its parent's value? Or to find an element by it's parent neighbour value?

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
annndrey
  • 1,768
  • 2
  • 20
  • 24

1 Answers1

5

The .xpath method let's you find tags by XPath 1.0 expressions:

>>> from lxml import etree
>>> from cStringIO import StringIO
>>> tag2values = ['newvalue1', 'newvalue2']
>>> example = StringIO("""\
... <main_tag>
...    <first>
...      <tag1>val1</tag1>
...      <conf>
...        <tag2>val2</tag2>
...        <tag3>val3</tag3>
...        <tag4>val4</tag4>
...      </conf>
...    </first>
...    <second>
...      <tag1>val2</tag1>
...      <conf>
...        <tag2>val6</tag2>
...        <tag3>val7</tag3>
...        <tag4>val8</tag4>
...      </conf>
...    </second>
... </main_tag>
... """)
>>> tree = etree.parse(example)
>>> value1selector = '*/conf/tag2[../../tag1/text() = "val1"]'
>>> value2selector = '*/conf/tag2[../../tag1/text() = "val2"]'
>>> for elem in tree.xpath(value1selector):
...     elem.text = tag2values[0]
... 
>>> for elem in tree.xpath(value2selector):
...     elem.text = tag2values[1]
... 
>>> print(etree.tostring(tree, pretty_print=True))
<main_tag>
   <first>
     <tag1>val1</tag1>
     <conf>
       <tag2>newvalue1</tag2>
       <tag3>val3</tag3>
       <tag4>val4</tag4>
     </conf>
   </first>
   <second>
     <tag1>val2</tag1>
     <conf>
       <tag2>newvalue2</tag2>
       <tag3>val7</tag3>
       <tag4>val8</tag4>
     </conf>
   </second>
</main_tag>

In the above example, the XPath expression in value1selector gives you all tag2 elements that are children of conf, with a sibling tag1 tag with text val1, as ElementTree Element instances, thus making it trivial to replace their text content.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • I'd like to know if there is a way to find an element in this way: `tag1="val1"/tag2` because it can be a situation when we need to chance just one of tag2 depending to it's parent tag value – annndrey Jun 16 '12 at 10:33
  • Updated the answer; note that you are now essentially asking XPath questions such as http://stackoverflow.com/questions/912194/matching-a-node-based-on-a-siblings-value-with-xpath – Martijn Pieters Jun 16 '12 at 12:32