pythonlxmlelementtreexml.etree

Adding a subelement iteratively with lxml


I want to add subelements iteratively e. g. from a list 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.

For my provided example, I tried:

from lxml import etree

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

new_subelements = ['<year></year>', '<trackNumber></trackNumber>']

destination = root.xpath('/interpretLibrary/interprets/interpretNames/interpret[2]/information')[2]

for element in new_subelements:
    add_element = etree.fromstring(element)
    destination.insert(-1, add_element)

But this doesn't work.

The initial example.xml file:

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

The output example.xml I want to produce:

<interpretLibrary>
    <interprets>
        <interpretNames>
            <interpret>
                <information>
                    <name>Queen</name>
                    <album></album>
                </information>
            <interpret>
            <interpret>
                <information>
                    <name>Michael Jackson</name>
                    <album></album>
                </information>
            <interpret>
            <interpret>
                <information>
                    <name>U2</name>
                    <album></album>
                    <year></year>
                    <trackNumber></trackNumber>
                </information>
            <interpret>
        </interpretNames>
    </interprets>
</interpretLibrary>

Is there any better solution?


Solution

  • Your sample xml in the question is not well formed (the closing <interpret> elements should be closed - </interpret>). Assuming you fixed that, you are almost there - though some changes are necessary:

    new_subelements = ['<year></year>', '<trackNumber></trackNumber>']
    for element in new_subelements:
        destination = root.xpath('//interpretNames/interpret[last()]/information/*[last()]')[0]
        add_element = etree.fromstring(element)
        destination.addnext(add_element)
    print(etree.tostring(root).decode())
    

    The output should be your sample expected output.