3

I've been trying to edit one specific element content in an XML which contains multiple element contents of the same name, but the "for loop" which is required to set the element attribute will always go through the entire section and change them all.

Let's say that this is my XML:

<SectionA>
    <element_content attribute="device_1" type="parameter_1" />
    <element_content attribute="device_2" type="parameter_2" />
</SectionA>

I am currently using ElementTree with this code which works perfectly when a certain section has element content with different names, but it does not work for such a case - where the name is the same. It will simply change all of the content's attributes to have the same value.

for element in root.iter(section):
    print element
    element.set(attribute, attribute_value)

How do I access a specific element content and only change that one?

Bear in mind that I have no knowledge of the currently present attributes inside the element_content section, as I am dynamically adding them to a user's request.

Edit: Thanks to @leovp I was able to work around my problem and came up with this solution:

for step in root.findall(section):
    last_element = step.find(element_content+'[last()]')

last_element.set(attribute, attribute_value)

This causes the for loop to always change the last attribute in the specific nest. Since I am dynamically adding and editing lines, this makes it change the last one I have added.

Thank you.

1 Answer 1

2

You can use limited XPath support that xml.etree provides:

>>> from xml.etree import ElementTree
>>> xml_data = """
... <SectionA>
...     <element_content attribute="device_1" type="parameter_1" />
...     <element_content attribute="device_2" type="parameter_2" />
... </SectionA>
... """.strip()
>>> tree = ElementTree.fromstring(xml_data)
>>> d2 = tree.find('element_content[@attribute="device_2"]')
>>> d2.set('type', 'new_type')
>>> print(ElementTree.tostring(tree).decode('utf-8'))
<SectionA>
    <element_content attribute="device_1" type="parameter_1" />
    <element_content attribute="device_2" type="new_type" />
</SectionA>

The most important part here is an XPath expression, where we find an element by its name AND attribute value:

d2 = tree.find('element_content[@attribute="device_2"]')

Update: since the XML data in question is not known beforehand. You can query the first, second, ..., last elements like this (indices start from 1):

tree.find('element_content[1]')
tree.find('element_content[2]')
tree.find('element_content[last()]')

But since you're iterating over elements anyway, the most simple solution is to just check current element's attributes:

for element in root.iter(section):
    if element.attrib.get('type') == 'parameter_2'):
        element.set(attribute, attribute_value)
Sign up to request clarification or add additional context in comments.

3 Comments

Hey, thank you very much for your answer! Unfortunately, I can't search this way because I can't tell what is the attribute's value. The XML file is "invisible" to me, and I should be able to edit it dynamically. If I'd want to change the 1st/2nd element_conent, is there no way to check for element_content[0] or something of that sort?
I updated the answer with a couple of possible solutions.
The for loop you have offered did not assist, since as I have said, I am unable to determine the attribute that is located inside the element. I did, however, worked it out eventually using part of your solution, mostly the [last()] section. I have updated my original post with the change. Thank you very much for your assistance!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.