1

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?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Jack Fleeting
  • 24,385
  • 6
  • 23
  • 45

1 Answers1

1

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>
Daniel Haley
  • 51,389
  • 6
  • 69
  • 95
  • 1
    Well, I'll be damned! It never occurred to me to use that property of Element to construct one from a dictionary... Live and learn! Thank you, kind sir. – Jack Fleeting Dec 23 '20 at 21:22