1

I have an element in an XML file as:

<condition>
  <comparison compare="and">
    <operand idref="XXX" type="boolean" />
  </comparison>
</condition>

I need to add two other child elements (child1 and child2) such as:

<condition>
  <child1 compare='and'>
    <child2 idref='False' type='int' /> 
    <comparison compare="and">
      <operand idref="XXX" type="boolean" />
    </comparison>
  </child1>
</condition>

I proceeded using lxml:

from lxml import etree
tree = etree.parse(xml_file)
condition_elem = tree.find("<path for the condition block in the xml>")
etree.SubElement(condition_elem, 'child1')
tree.write( 'newXML.xml', encoding='utf-8', xml_declaration=True)

This just adds the element child1 as a child of the element condition as below and did not satisfy my requirement:

<condition>
  <child1></child1>
  <comparison compare="and">
    <operand idref="XXX" type="boolean" />
  </comparison>
</condition>

Any ideas? Thanks

1 Answer 1

1

Using lxml's objectify submodule over it's etree submodule, I would cut out the comparison element from root, add the child1 element to it and the stuff comparison back in to that:

from lxml import objectify

tree = objectify.parse(xml_file)
condition = tree.getroot()
comparison = condition.comparison

M = objectify.ElementMaker(annotate=False)
child1 = M("child1", {'compare': 'and'})
child2 = M("child2", {'idref': 'False', 'type': 'int'})

condition.remove(comparison)
condition.child1 = child1
condition.child2 = child2
condition.child1.comparison = comparison

The ElementMaker is an easy-to-use tool to create new xml elements. I first an instance of it (M) that doesn't annotate the xml (litter it with attributes), then use that instance to create the children, you ask for. The rest is fairly self explanatory, I think.

3
  • One word about the tag names, because objectify is kinda funny about those. When I create the child1 and child2 elements, it is the first argument that determines the tag name, i.e. the quoted "child1"/"child2". For clarity I'm just using the same names for the python variables. However, when I'm adding those elements to the 'condition' element, their tag name change to that of the attribute. If I had used "condition.evil_spawn = child1", the tag name of the child of condition would be "evil_spawn", not child. Since we're still using 'child1' here, nothing changes.
    – brokkr
    Commented Nov 15, 2017 at 11:02
  • What would I do if I have multiple <comparison> elements in the same level? Commented Nov 15, 2017 at 11:12
  • comparison = condition.comparison targets the first element with the tag name of 'comparison'. To get a list of the comparison elements do condition.comparison[:].
    – brokkr
    Commented Nov 15, 2017 at 11:30

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.