pythonxmllxmlelementtreexml.etree

Inserting text into an XML-file element using xpath


I want to add an element to a specific position of an XML path using XPath and a tag. Since in my original code I have many subnodes, I don't want to use other functions such as etree.Element or tree.SubElement.

from lxml import etree

tree = etree.parse('example.xml')
root = tree.getroot()

destination = root.xpath('interprets/interpretNames/interpret[2]/album')[0]
destination.insert(0, 'Thriller')

The initial example.xml file:

<interprets>
    <interpretNames>
        <interpret>
            <name>Queen</name>
            <album></album>
        <interpret>
        <interpret>
            <name>Michael Jackson</name>
            <album></album>
        <interpret>
        <interpret>
            <name>U2</name>
            <album></album>
        <interpret>
    </interpretNames>
</interprets>

The example.xml file after the element was created and inserted:

<interprets>
    <interpretNames>
        <interpret>
            <name>Queen</name>
            <album></album>
        <interpret>
        <interpret>
            <name>Michael Jackson</name>
            <album>Thriller</album>
        <interpret>
        <interpret>
            <name>U2</name>
            <album></album>
        <interpret>
    </interpretNames>
</interprets>

However, when I run my original code, I get an:

 IndexError: list index out of range

I double-checked to make sure the subpaths at the tag position exist, and they do. What am I missing?


Solution

  • Foremost, there is an issue in your example.xml file. If you carefully read the error message, you'll see that the closing tags are missing.

    Here is the fixed file:

    <interprets>
        <interpretNames>
            <interpret>
                <name>Queen</name>
                <album></album>
            </interpret>             <!-- HERE -->
            <interpret>
                <name>Michael Jackson</name>
                <album></album>
            </interpret>             <!-- HERE -->
            <interpret>
                <name>U2</name>
                <album></album>
            </interpret>             <!-- HERE -->
        </interpretNames>
    </interprets>
    

    (HERE shows where you used opening tags instead of closing tags)

    After fixing the XML file, the next error message tells you that there is an index error in line 6. Here you should remove the dangling [0] and the error message will disappear. But you are not done yet, by inspecting destination like here

    print(destination)
    destination.insert(0, 'Thriller')
    print(destination)
    

    you'll see

    []
    ['Thriller']
    

    that destination has changed.

    But destination is plain Python list object. That's why you cannot modify the tree through it.

    So now, after a bit of (re)seach, I'd suggest fixing it this way:

    from lxml import etree
    
    tree = etree.parse('example.xml')
    el = tree.find('interpretNames/interpret[2]/album')
    el.text = 'Thriller'
    tree.write('example-mod.xml')
    

    The text attribute is already present in: xml.etree.ElementTree for which lxml's etree is an extension.

    I can understand your wish, it may work by means of xpath. But if it is not possible, maybe you should accept that? One can also wish that one can take a certain commodity from the supermarket without paying, but this wish does not lead far.