allankey allankey - 1 year ago 59
Python Question

Python easier way to insert new items into XML sub sub elements using ElementTree

I am editing a config file and would like to add new sub elements into the structure. The problem I am having is that in the config file the sub elements have the same name and I have not found a way to find the index the insert location with a .find command. An example of the config file is here:

<?xml version="1.0" encoding="utf-8"?>

<Configuration ItemName="Parent1">
<Category ItemName="Parent2">
<Category ItemName="Parent3">
<Category ItemName="ChildLevelToAdd">
<Option ItemName="x" ValueType="6">5</Option>
<Option ItemName="z" ValueType="6">0</Option>
<Category ItemName="ChildCategory">
<Option ItemName="a" ValueType="1"></Option>
<Option ItemName="b" ValueType="2"></Option>
<Option ItemName="c" ValueType="3">

I am trying to add a new "ChildToAdd" category and everything that falls below it.

I have tried the following code to index, but it seems it is only able to locate first child elements:

a = root.find('Configuration') #It is only possible to find sub-elements of the root
b = root.find('Category') #These generate a none type

The best way I have found to insert the new category element is using:

root[0][0][0].insert(0,ET.Element("Category", ItemName = "NewChild"))

And then when I want to add options to this newly created category I use:

root[0][0][0][0].insert(0,ET.Element("NewOption", ItemName = "NewVal", ValueType ="NewType"))

This seems like a very untidy way to do this. I am wondering if there are any cleaner methods to find and specify the insertion point for the new subsubelement (category) and its future sub-elements (options)?

Answer Source

Use XPath expression in the form of .//element_name to find element anywhere within context node (the root element in this case). For example, the following should return first instance of <Category> element, anywhere within root :


You can also find element by its attribute value using XPath. For example, the following should return <Category> element where ItemName attribute value equals "Parent3" :


ElementTree supports some other XPath syntax as documented in here.

I'd also suggest to store your new element in a variable so that you can add child element to it later without having to query the XML again :

c = ET.Element("Category", ItemName = "NewChild")
c.insert(0,ET.Element("NewOption", ItemName = "NewVal", ValueType ="NewType"))

p = root.find('.//Category[@ItemName="Parent3"]')
p.insert(0, c)