25

I am using python xmlElementTree and want to assign or modify a xml element value based on its attribute. Can somebody give me an idea how to do this?

For example: Here is a xml file and I need to set the value for the element "number" based on the attribute "sys/phoneNumber/1", "sys2/SMSnumber/1" and so on.

<root>
    <phoneNumbers>
        <number topic="sys/phoneNumber/1" update="none" />
        <number topic="sys/phoneNumber/2" update="none" />
        <number topic="sys/phoneNumber/3" update="none" />
    </phoneNumbers>

    <gfenSMSnumbers>
        <number topic="sys2/SMSnumber/1" update="none" />
        <number topic="sys2/SMSnumber/2" update="none" />
    </gfenSMSnumbers>
</root>

edit: Added closure for the tag root in the XML file.

1stthomas
  • 731
  • 2
  • 15
  • 22
user1282251
  • 251
  • 1
  • 3
  • 3

4 Answers4

26

You can access the attribute value as this:

from elementtree.ElementTree import XML, SubElement, Element, tostring

text = """
<root>
    <phoneNumbers>
        <number topic="sys/phoneNumber/1" update="none" />
        <number topic="sys/phoneNumber/2" update="none" />
        <number topic="sys/phoneNumber/3" update="none" />
    </phoneNumbers>

    <gfenSMSnumbers>
        <number topic="sys2/SMSnumber/1" update="none" />
        <number topic="sys2/SMSnumber/2" update="none" />
    </gfenSMSnumbers>
</root>
"""

elem = XML(text)
for node in elem.find('phoneNumbers'):
    print node.attrib['topic']
    # Create sub elements
    if node.attrib['topic']=="sys/phoneNumber/1":
        tag = SubElement(node,'TagName')
        tag.attrib['attr'] = 'AttribValue'

print tostring(elem)

forget to say, if your ElementTree version is greater than 1.3, you can use XPath:

elem.find('.//number[@topic="sys/phoneNumber/1"]')

http://effbot.org/zone/element-xpath.htm

or you can use this simple one:

for node in elem.findall('.//number'):
    if node.attrib['topic']=="sys/phoneNumber/1":
        tag = SubElement(node,'TagName')
        tag.attrib['attr'] = 'AttribValue'
focusheart
  • 347
  • 2
  • 3
  • You showed how to locate the element and modify the attribute but he asked how modify the element. I think your last line should be: tag.text = 'new value' – Spaceghost Mar 21 '12 at 01:50
  • Yes, I didn't modify the element's value...create a new tag and assign value: `tag.text = 'new value'` or modify the element value: `node.text = 'new value'` – focusheart Mar 21 '12 at 02:17
  • what if i want to use a xml file as input instead of text? – user1282251 Mar 21 '12 at 03:18
  • You can read the file: `f = open('file.xml')` ; Read all text into a variable: `text = f.read()` ; and close the file: `f.close()` – focusheart Mar 21 '12 at 03:22
19

For me this Elementtree snipped of code worked to find element by attribute:

import xml.etree.ElementTree as ET
tree = ET.parse('file.xml')
root = tree.getroot()


topic=root.find(".//*[@topic='sys/phoneNumber/1']").text
Eduard Florinescu
  • 16,747
  • 28
  • 113
  • 179
3

I'm not familiar with xmlElementTree, but if you're using something capable of xpath expressions you can locate a node by attribute value using an expression like this:

//number[@topic="sys/phoneNumber/1"]

So, using the etree module:

>>> import lxml.etree as etree
>>> doc = etree.parse('foo.xml')
>>> nodes = doc.xpath('//number[@topic="sys/phoneNumber/1"]')
>>> nodes
[<Element number at 0x10348ed70>]
>>> etree.tostring(nodes[0])
'<number topic="sys/phoneNumber/1" update="none"/>\n    '
larsks
  • 277,717
  • 41
  • 399
  • 399
1

larsks explains how to use XPath to find what you are after very well. You also wanted to change an attribute. The best way is probably to add a new attribute and remove the original. Once you get the nodes result, it is a list with a single entry (number).

# This returns sys/phoneNumber/1
nodes[0].get("topic")
# To change the value, use set 
nodes[0].set("topic", "new/value/of/phone/number")

Hope this helps.

Also, your ending root tag doesn't close properly.

calmond
  • 189
  • 7