web-dev-qa-db-fra.com

Jolie impression XML en Python

Quel est le meilleur moyen (ou même les différents moyens) d’imprimer joliment xml en Python?

364
Hortitude
import xml.dom.minidom

dom = xml.dom.minidom.parse(xml_fname) # or xml.dom.minidom.parseString(xml_string)
pretty_xml_as_string = dom.toprettyxml()
331
Ben Noland

lxml est récent, mis à jour et inclut une jolie fonction d'impression

import lxml.etree as etree

x = etree.parse("filename")
print etree.tostring(x, pretty_print=True)

Découvrez le tutoriel lxml: http://lxml.de/tutorial.html

141
1729

Une autre solution consiste à emprunter cette fonction indent , à utiliser avec la bibliothèque ElementTree intégrée à Python depuis la version 2.5 .

from xml.etree import ElementTree

def indent(elem, level=0):
    i = "\n" + level*"  "
    j = "\n" + (level-1)*"  "
    if len(elem):
        if not elem.text or not elem.text.strip():
            elem.text = i + "  "
        if not elem.tail or not elem.tail.strip():
            elem.tail = i
        for subelem in elem:
            indent(subelem, level+1)
        if not elem.tail or not elem.tail.strip():
            elem.tail = j
    else:
        if level and (not elem.tail or not elem.tail.strip()):
            elem.tail = j
    return elem        

root = ElementTree.parse('/tmp/xmlfile').getroot()
indent(root)
ElementTree.dump(root)
94
ade

Voici ma (hacky?) Solution pour résoudre le problème des nœuds de texte laids.

uglyXml = doc.toprettyxml(indent='  ')

text_re = re.compile('>\n\s+([^<>\s].*?)\n\s+</', re.DOTALL)    
prettyXml = text_re.sub('>\g<1></', uglyXml)

print prettyXml

Le code ci-dessus produira:

<?xml version="1.0" ?>
<issues>
  <issue>
    <id>1</id>
    <title>Add Visual Studio 2005 and 2008 solution files</title>
    <details>We need Visual Studio 2005/2008 project files for Windows.</details>
  </issue>
</issues>

Au lieu de cela:

<?xml version="1.0" ?>
<issues>
  <issue>
    <id>
      1
    </id>
    <title>
      Add Visual Studio 2005 and 2008 solution files
    </title>
    <details>
      We need Visual Studio 2005/2008 project files for Windows.
    </details>
  </issue>
</issues>

Avertissement: Il y a probablement quelques limitations.

46
Nick Bolton

Comme d'autres l'ont fait remarquer, lxml a une jolie imprimante intégrée.

Sachez cependant que, par défaut, les sections CDATA changent en texte normal, ce qui peut avoir des résultats désagréables.

Voici une fonction Python qui préserve le fichier d’entrée et ne modifie que l’indentation (remarquez le strip_cdata=False). De plus, il s'assure que la sortie utilise l'encodage UTF-8 au lieu de la valeur par défaut ASCII (remarquez le encoding='utf-8'):

from lxml import etree

def prettyPrintXml(xmlFilePathToPrettyPrint):
    assert xmlFilePathToPrettyPrint is not None
    parser = etree.XMLParser(resolve_entities=False, strip_cdata=False)
    document = etree.parse(xmlFilePathToPrettyPrint, parser)
    document.write(xmlFilePathToPrettyPrint, pretty_print=True, encoding='utf-8')

Exemple d'utilisation:

prettyPrintXml('some_folder/some_file.xml')
19
roskakori

BeautifulSoup a une méthode facile à utiliser prettify()

Il indente un espace par niveau d'indentation. Cela fonctionne beaucoup mieux que pretty_print de lxml et est court et simple. 

from bs4 import BeautifulSoup

bs = BeautifulSoup(open(xml_file), 'xml')
print bs.prettify()
12
ChaimG

Si vous avez xmllint, vous pouvez générer un sous-processus et l’utiliser. xmllint --format <file> pretty-imprime son XML d'entrée sur la sortie standard.

Notez que cette méthode utilise un programme externe à python, ce qui en fait une sorte de piratage.

def pretty_print_xml(xml):
    proc = subprocess.Popen(
        ['xmllint', '--format', '/dev/stdin'],
        stdin=subprocess.PIPE,
        stdout=subprocess.PIPE,
    )
    (output, error_output) = proc.communicate(xml);
    return output

print(pretty_print_xml(data))
10
Russell Silva

J'ai essayé d'éditer la réponse de "ade" ci-dessus, mais Stack Overflow ne m'a pas laissé éditer après que j'avais initialement fourni des commentaires anonymement. Ceci est une version moins boguée de la fonction permettant d’imprimer joliment un ElementTree.

def indent(elem, level=0, more_sibs=False):
    i = "\n"
    if level:
        i += (level-1) * '  '
    num_kids = len(elem)
    if num_kids:
        if not elem.text or not elem.text.strip():
            elem.text = i + "  "
            if level:
                elem.text += '  '
        count = 0
        for kid in elem:
            indent(kid, level+1, count < num_kids - 1)
            count += 1
        if not elem.tail or not elem.tail.strip():
            elem.tail = i
            if more_sibs:
                elem.tail += '  '
    else:
        if level and (not elem.tail or not elem.tail.strip()):
            elem.tail = i
            if more_sibs:
                elem.tail += '  '
10
Joshua Richardson

Si vous utilisez une implémentation DOM, chacun a sa propre forme d’impression jolie intégrée:

# minidom
#
document.toprettyxml()

# 4DOM
#
xml.dom.ext.PrettyPrint(document, stream)

# pxdom (or other DOM Level 3 LS-compliant imp)
#
serializer.domConfig.setParameter('format-pretty-print', True)
serializer.writeToString(document)

Si vous utilisez quelque chose d'autre sans sa jolie imprimante - ou si ces jolies imprimantes ne le font pas comme vous le souhaitez -, vous devrez probablement écrire ou sous-classer votre propre sérialiseur.

8
bobince

J'ai eu quelques problèmes avec la jolie impression de minidom. J'obtenais un UnicodeError chaque fois que j'essayais d'imprimer un document avec des caractères en dehors du codage donné, par exemple si j'avais un β dans un document et que j'essayais doc.toprettyxml(encoding='latin-1'). Voici ma solution de contournement pour cela:

def toprettyxml(doc, encoding):
    """Return a pretty-printed XML document in a given encoding."""
    unistr = doc.toprettyxml().replace(u'<?xml version="1.0" ?>',
                          u'<?xml version="1.0" encoding="%s"?>' % encoding)
    return unistr.encode(encoding, 'xmlcharrefreplace')
6
giltay
from yattag import indent

pretty_string = indent(ugly_string)

Il ne va pas ajouter d'espaces ou de nouvelles lignes à l'intérieur des nœuds de texte, à moins que vous ne le demandiez avec:

indent(mystring, indent_text = True)

Vous pouvez spécifier quelle unité d'indentation doit être et à quoi devrait ressembler la nouvelle ligne.

pretty_xml_string = indent(
    ugly_xml_string,
    indentation = '    ',
    newline = '\r\n'
)

La doc se trouve sur http://www.yattag.org homepage.

5

J'ai écrit une solution pour parcourir un ElementTree existant et utiliser text/tail pour l'indenter comme on s'y attend généralement.

def prettify(element, indent='  '):
    queue = [(0, element)]  # (level, element)
    while queue:
        level, element = queue.pop(0)
        children = [(level + 1, child) for child in list(element)]
        if children:
            element.text = '\n' + indent * (level+1)  # for child open
        if queue:
            element.tail = '\n' + indent * queue[0][0]  # for sibling open
        else:
            element.tail = '\n' + indent * (level-1)  # for parent close
        queue[0:0] = children  # prepend so children come before siblings
3
nacitar sevaht

Une jolie impression XML pour python est plutôt jolie pour cette tâche. (Bien nommé, aussi.)

Une alternative consiste à utiliser pyXML , qui a la fonction PrettyPrint .

3
Daniel Lew

Jetez un coup d'oeil au module vkbeautify .

C'est une version python de mon très populaire plugin javascript/nodejs du même nom. Il peut joliment imprimer/réduire le texte XML, JSON et CSS. L'entrée et la sortie peuvent être chaîne/fichier dans n'importe quelle combinaison. Il est très compact et n’a aucune dépendance.

Exemples:

import vkbeautify as vkb

vkb.xml(text)                       
vkb.xml(text, 'path/to/dest/file')  
vkb.xml('path/to/src/file')        
vkb.xml('path/to/src/file', 'path/to/dest/file') 
2
vadimk

Vous pouvez utiliser la bibliothèque externe populaire xmltodict , avec unparse et pretty=True vous obtiendrez le meilleur résultat

xmltodict.unparse(
    xmltodict.parse(my_xml), full_document=False, pretty=True)

full_document=False contre <?xml version="1.0" encoding="UTF-8"?> en haut.

1
Vitaly Zdanevich

Une alternative si vous ne souhaitez pas reparse, il existe la bibliothèque xmlpp.py avec la fonction get_pprint(). Cela a fonctionné bien et sans heurts pour mes cas d'utilisation, sans avoir à réparer à un objet ElementTree lxml.

1
gaborous

J'ai eu ce problème et l'ai résolu comme ceci:

def write_xml_file (self, file, xml_root_element, xml_declaration=False, pretty_print=False, encoding='unicode', indent='\t'):
    pretty_printed_xml = etree.tostring(xml_root_element, xml_declaration=xml_declaration, pretty_print=pretty_print, encoding=encoding)
    if pretty_print: pretty_printed_xml = pretty_printed_xml.replace('  ', indent)
    file.write(pretty_printed_xml)

Dans mon code, cette méthode s'appelle comme ceci:

try:
    with open(file_path, 'w') as file:
        file.write('<?xml version="1.0" encoding="utf-8" ?>')

        # create some xml content using etree ...

        xml_parser = XMLParser()
        xml_parser.write_xml_file(file, xml_root, xml_declaration=False, pretty_print=True, encoding='unicode', indent='\t')

except IOError:
    print("Error while writing in log file!")

Cela ne fonctionne que parce que etree utilise par défaut two spaces pour indenter, ce que je ne trouve pas très accentué par l'indentation et donc pas joli Je ne pouvais définir aucun paramètre pour etree ni aucun paramètre pour aucune fonction permettant de modifier le retrait standard de etree. J'aime la facilité d'utilisation d'etree, mais cela m'a vraiment ennuyé.

0
Zelphir
from lxml import etree
import xml.dom.minidom as mmd

xml_root = etree.parse(xml_fiel_path, etree.XMLParser())

def print_xml(xml_root):
    plain_xml = etree.tostring(xml_root).decode('utf-8')
    urgly_xml = ''.join(plain_xml .split())
    good_xml = mmd.parseString(urgly_xml)
    print(good_xml.toprettyxml(indent='    ',))

Cela fonctionne bien pour le xml avec le chinois!

0
Reed_Xia