Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Write .xml in Python with pretty print and encoding declaration

I have to create an .xml file that has pretty print and also the encoding declaration. It should look like this: like this:

<?xml version='1.0' encoding='utf-8'?>
<main>
    <sub>
        <name>Ana</name>
        <detail />
        <type>smart</type>
    </sub>
</main>

I know how to get the pretty print and the declaration, but not at the same time. To obtain the UTF-8 declaration, but no pretty print, I use the code below:

f = open(xmlPath, "w")
et.write(f, encoding='utf-8', xml_declaration=True) 
f.close()

But if I want to get the pretty print, I have to convert the xml tree into string, and I will lose the declaration. I use this code:

from xml.dom import minidom
xmlstr = minidom.parseString(ET.tostring(root)).toprettyxml(indent="   ")
with open(xmlPath, "w") as f:
    f.write(xmlstr.encode('utf-8'))
    f.close()

With this last code, I get the pretty print, only that the first row is:

<?xml version="1.0" ?>

I might just as well replace this with

<?xml version='1.0' encoding='utf-8'?>

but I don't find this to be the most pythonesque method.

I use the xml module and I prefer not to install extra modules because the script has to be run from various computers with standard Python. But if it's not possible, I will install other modules.

Later Edit:

In the end, with Lenz's help, I use this:

#ET=lxml.etree
xmlPath=os.path.join(output_folderXML ,"test.xml")
xmlstr= ET.tostring(root, encoding='UTF-8', xml_declaration=True, pretty_print=True)
with open(xmlPath, "w") as f:
    f.write(xmlstr)
    f.close()

I need to know if it is safe to write the result of the "tostring" method to the .xml file in the "w" mode, not "wb". As I said in one of the comments below, with "wb" I don't get the pretty print when I open the xml file in Notepad, but with "w", I do. Also, I have checked the xml file written in "w" mode and the special characters like "ü" are there. I only need an competent opinion that what I do is technically OK.

like image 841
panda Avatar asked Nov 27 '17 20:11

panda


Video Answer


2 Answers

The most elegant solution is certainly using the third-party library lxml, which is being used a lot – for good reasons. It offers both a pretty_print and an xml_declaration parameter in the tostring() method, so you get both. And the API is quite close to that of the std-lib ElementTree, which you seem to be using now. Here's an example:

>>> from lxml import etree
>>> doc = etree.parse(xmlPath)
>>> print etree.tostring(doc, encoding='UTF-8', xml_declaration=True,
                         pretty_print=True)
<?xml version='1.0' encoding='UTF-8'?>
<main>
  <sub>
    <name>Ana</name>
    <detail/>
    <type>smart</type>
  </sub>
</main>

However, I understand your desire to use the "included batteries" only. As far as I can see, xml.etree.ElementTree has no means of changing the indentation automatically. But the minidom work-around has a solution to getting both pretty-printing and a full declaration: use the encoding parameter of the toprettyxml() method!

>>> doc = minidom.parseString(ET.tostring(root))
>>> print doc.toprettyxml(encoding='utf8')
<?xml version="1.0" encoding="utf8"?>
<main>
    <sub>
        <name>Ana</name>
        <detail/>
        <type>smart</type>
    </sub>
</main>

(Be aware that the returned string is already encoded and that you should write it to a file opened in binary mode ("wb") and without further encoding.)

like image 105
lenz Avatar answered Sep 21 '22 00:09

lenz


from xml.dom import minidom
xmlstr = minidom.parseString(ET.tostring(root)).toprettyxml(indent="   ", encoding='UTF-8')
with open(xmlPath, "w") as f:
    f.write(str(xmlstr.decode('UTF-8')))
    f.close()

Probably This will resolve your issue without using external libraries like lxml

like image 36
Krushnal Dhandhukia Avatar answered Sep 20 '22 00:09

Krushnal Dhandhukia