11

I successfully added a new node to an Element using PY's ElementTree. I now try to give it attributes, which fails, despite I'm following the tutorial.

my example xml:

<?xml version="1.0" encoding="UTF-8"?>
<xml>
<level01>
<level02>
<level03>
<level04>
<node q="3,4,5,7,8" p="zen"/>
<node q="a,s,e,o,l" p="zen"/>
</level04>
</level03>
# >> here will be the new node, called <subi/> <<   
<level03>
<level04>
<node q="x,y" p="zen"/>
<node q="xxx,yyy" p="zen"/>
</level04>
</level03>
</level02>
</level01>
</xml>

The node is created like this:

subi = ETL.SubElement(root[0][0][1][0][0], 'subi')

which works, it can then be accessed via root001000 and it's tag can be read.

but I fail trying to add attributes.

I tried using the syntax I found in another thread here: (with my names ofc)

>>> myattributes = {"size": "small", "gender": "unknown"}
>>> child = ET.SubElement(parent, "child", attrib=myattributes, age="10" )

Also I tried it directly, like

subi = ETL.SubElement(root[0][0][1][0][0], 'subi', attrib={"size": "small", "gender": "unknown"})

Results are always

root[0][0][1][0][0][0].tag
'subi'

but

root[0][0][1][0][0][0].attrib
{}

I also found how lxml does it, but this does not work with elementtree

#Any keyword arguments of the form name=value that you supply to the constructor are added #to the element's attributes. For example, this code:

newReed = etree.Element('reed', pitch='440', id='a4')

#will produce an element that looks like this:

<reed pitch='440' id='a4'/>

What am I doing wrong? How can I do it right? Is there a way to make elementtree do it? Or do I have to use lxml? (which would be dispreferred) ?

sibert
  • 427
  • 2
  • 5
  • 10
  • The `SubElement(parent,tag,attrib={name:key})` syntax works fine for me (Python 2.7.5). Can you provide an example against a short two level XML doc, the exact insertion code and the resulting XML output? – isedev Sep 12 '14 at 11:49
  • as posted in the comment below, I can now obtain results by doint subi.attrib. Somehow the root[index].attrib did fail... – sibert Sep 12 '14 at 12:28
  • Possible duplicate of [How do I set attributes for an XML element with Python?](https://stackoverflow.com/questions/18796280/how-do-i-set-attributes-for-an-xml-element-with-python) – Stevoisiak Feb 14 '18 at 15:03

2 Answers2

25

Isn't this you are trying to do? Assuming that subi is your element and you can access it, you can further use ElementTree method set:

subi.set(attr, value)

Look here about set method of ElementTree : https://docs.python.org/2/library/xml.etree.elementtree.html#xml.etree.ElementTree.Element.set

German Petrov
  • 1,475
  • 1
  • 17
  • 21
  • Setting attrib values can be done with `SubElement`. The OP should not have to set them after creating the new element (although that is also possible, for sure). – isedev Sep 12 '14 at 11:51
  • I think I want it done in SubElement, but set looks OK too - what would be the exact syntax in my case? If I just do subi.set(xy, "3") it will state that name 'xy' is not defined – sibert Sep 12 '14 at 12:20
  • you should `subi.set("xy", "3")` it should make `subi` tag as `` – German Petrov Sep 12 '14 at 12:24
  • ok, that works. Curiously enough, now also the attribs I tried to set before are displayed. Is there a reason why root000101.attrib won't work and subi.attrib does? (assuming root indexes are spelt properly) – sibert Sep 12 '14 at 12:27
  • intersting, for me it worked OK: after setting attribute and value, `print tree[0][0][1][0][0][0].tag` gives `subi` and `print tree[0][0][1][0][0][0].attrib` gives `{'xy': '3'}` – German Petrov Sep 12 '14 at 12:35
  • Hi, I want to set a value to the SubElement like below `Stories`. Any idea on how to set the value to the SubElement – Sai Ram Reddy Dec 10 '19 at 08:59
13

You can specify attributes for an Element or SubElement during creation with keyword arguments.

import xml.etree.ElementTree as ET

root = ET.Element('Summary')
ET.SubElement(root, 'TextSummary', Status='Completed')
# <Summary><TextSummary Status="Completed" /></Summary>

Alternatively, you can use .set to add an attribute to an element after creation.

import xml.etree.ElementTree as ET

root = ET.Element('Summary')
sub = ET.SubElement(root, 'TextSummary')
sub.set('Status', 'Completed')
# <Summary><TextSummary Status="Completed" /></Summary>

Generated XML:

<Summary>
    <TextSummary Status="Completed"/>
</Summary>

Explanation:

The constructors for Element and SubElement include **extra, which accepts attributes as keyword arguments.

xml.etree.ElementTree.Element(tag, attrib={}, **extra)
xml.etree.ElementTree.SubElement(parent, tag, attrib={}, **extra)

This allows you to add an arbitrary number of attributes.

root = ET.Element('Summary', Date='2018/07/02', Timestamp='11:44am')
# <Summary Date = "2018/07/02" Timestamp = "11:44am">

You can also use use .set to add attributes to a pre-existing element. However, this can only add one element at a time. (Suggested by German Petrov).

root = ET.Element('Summary')
root.set('Date', '2018/07/02')
root.set('Timestamp', '11:44am')
# <Summary Date = "2018/07/02" Timestamp = "11:44am">

Full Example:

import xml.etree.ElementTree as ET

root = ET.Element('school', name='Willow Creek High')
ET.SubElement(root, 'student', name='Jane Doe', grade='9')
print(ET.tostring(root).decode())
# <school name="Willow Creek High"><student grade="9" name="Jane Doe" /></school>
Stevoisiak
  • 23,794
  • 27
  • 122
  • 225