Reputation: 24930
For some reason, I found it difficult to generate an XML file by inserting multiple nodes from inside a for
loop. The following does achieve that, but a couple of questions remain.
Given a list of dictionaries
dict_list = [{'x': 'abc', 'y': 'efg'}, \
{'x': 'hij', 'y': 'klm'}]
I can use objectify()
to generate the desired xml:
from lxml import etree, objectify
end_game = etree.XML('<final_root/>')
E = objectify.ElementMaker(annotate=False)
tmp_root = E.entry
for d in dict_list:
att_values = [val for val in d.values()]
doc = tmp_root(
x = att_values[0],
y = att_values[1]
)
end_game.append(doc)
print(etree.tostring(end_game, pretty_print=True).decode())
Output, as desired:
<final_root>
<entry x="abc" y="efg"/>
<entry x="hij" y="klm"/>
</final_root>
The problem is that I need to hardwire the attribute names x
and y
into the loop. Attempting to use string interpolation fails. For instance:
for d in dict_list:
att_items = [item for item in d.items()]
doc = tmp_root(
att_items[0][0] = att_items[0][1],
att_items[1][0] = att_items[1][1]
)
raises
SyntaxError: expression cannot contain assignment, perhaps you meant "=="?
Same error is raised using f-strings ('f'{att_items[0][0]}' = att_items[0][1]
), or format ({}.format(att_items[0][0]) = att_items[0][1]
).
So, the obvious question: is there a way to avoid having to manually insert attribute names? Alternatively: Is it possible to duplicate the outcome (and maybe avoid the issue) using lxml.etree
instead?
Upvotes: 1
Views: 201
Reputation: 52858
Since etree Elements carry attributes as a dict, you should be able to just pass in the dict when constructing the Element...
from lxml import etree
end_game = etree.XML('<final_root/>')
dict_list = [{'x': 'abc', 'y': 'efg'},
{'x': 'hij', 'y': 'klm'}]
for meta in dict_list:
end_game.append(etree.Element('item', meta))
print(etree.tostring(end_game, pretty_print=True).decode())
printed output...
<final_root>
<item x="abc" y="efg"/>
<item x="hij" y="klm"/>
</final_root>
Upvotes: 1