it-swarm.com.de

Hübsches Drucken von XML in Python

Was ist der beste Weg (oder sogar die verschiedenen Möglichkeiten), XML in Python zu drucken?

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 ist aktuell, aktualisiert und enthält eine hübsche Druckfunktion

import lxml.etree as etree

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

Schauen Sie sich das lxml-Tutorial an: http://lxml.de/tutorial.html

141
1729

Eine andere Lösung ist das Ausleihen von dieser indent-Funktion , zur Verwendung mit der ElementTree-Bibliothek, die seit 2.5 ..__ in Python integriert ist.

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

Hier ist meine (hackige?) Lösung, um das Problem des hässlichen Textknotens zu umgehen.

uglyXml = doc.toprettyxml(indent='  ')

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

print prettyXml

Der obige Code wird Folgendes erzeugen:

<?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>

An Stelle von:

<?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>

Haftungsausschluss: Es gibt wahrscheinlich einige Einschränkungen.

46
Nick Bolton

Wie andere darauf hingewiesen haben, hat lxml einen hübschen Drucker eingebaut.

Beachten Sie jedoch, dass CDATA-Abschnitte standardmäßig in normalen Text geändert werden, was zu unangenehmen Ergebnissen führen kann.

Hier ist eine Python-Funktion, die die Eingabedatei beibehält und nur den Einzug ändert (beachten Sie den strip_cdata=False). Außerdem wird sichergestellt, dass die Ausgabe UTF-8 anstelle der Standardeinstellung ASCII als Kodierung verwendet (beachten Sie den 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')

Verwendungsbeispiel:

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

BeautifulSoup verfügt über eine einfach zu verwendende prettify()-Methode. 

Es rastet ein Leerzeichen pro Eindruckebene ein. Es funktioniert viel besser als der hübsche_druck von lxml und ist kurz und süß. 

from bs4 import BeautifulSoup

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

Wenn Sie xmllint haben, können Sie einen Unterprozess erzeugen und verwenden. xmllint --format <file> druckt die XML-Eingabedatei in die Standardausgabe.

Beachten Sie, dass diese Methode ein Programm außerhalb von Python verwendet, wodurch es zu einem Hack wird.

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

Ich habe versucht, die Antwort von "ade" oben zu bearbeiten, aber Stack Overflow ließ mich nicht bearbeiten, nachdem ich anfänglich anonym Feedback gegeben hatte. Dies ist eine weniger fehlerhafte Version der Funktion, um einen ElementTree zu drucken.

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

Wenn Sie eine DOM-Implementierung verwenden, verfügt jede über ihre eigene integrierte Form des hübschen Druckens:

# 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)

Wenn Sie etwas anderes ohne einen eigenen hübschen Drucker verwenden - oder diese hübschen Drucker nicht ganz so machen, wie Sie möchten - müssen Sie wahrscheinlich Ihren eigenen Serialiser schreiben oder eine Unterklasse erstellen.

8
bobince

Ich hatte einige Probleme mit dem hübschen Druck von Minidom. Ich würde einen UnicodeError erhalten, wenn ich ein Dokument mit Zeichen außerhalb der angegebenen Kodierung hübsch gedruckt habe, z. B. wenn ich ein β in einem Dokument hatte und doc.toprettyxml(encoding='latin-1') versuchte. Hier ist meine Problemumgehung:

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)

Es fügt keine Leerzeichen oder Zeilenumbrüche in Textknoten ein, es sei denn, Sie fragen mit:

indent(mystring, indent_text = True)

Sie können festlegen, wie die Einzugseinheit aussehen soll und wie der Zeilenumbruch aussehen soll.

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

Das Dokument befindet sich auf http://www.yattag.org homepage.

5

Ich habe eine Lösung geschrieben, um durch einen vorhandenen ElementTree zu gehen und ihn mit Text/Tail einzurücken, wie es normalerweise erwartet wird.

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

XML ​​Pretty Print für Python sieht für diese Aufgabe ziemlich gut aus. (Auch entsprechend benannt.)

Eine Alternative ist die Verwendung von pyXML , die eine PrettyPrint-Funktion hat.

3
Daniel Lew

Schauen Sie sich das Modul vkbeautify an.

Es ist eine Python-Version meines sehr beliebten Javascript/nodejs-Plugins mit demselben Namen. Es kann XML-, JSON- und CSS-Text hübsch drucken/minimieren. Eingabe und Ausgabe können aus Zeichenfolge/Datei in beliebigen Kombinationen bestehen. Es ist sehr kompakt und hat keinerlei Abhängigkeiten.

Beispiele:

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

Sie können die beliebte externe Bibliothek xmltodict verwenden. Mit unparse und pretty=True erhalten Sie das beste Ergebnis:

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

full_document=False gegen <?xml version="1.0" encoding="UTF-8"?> oben.

1

Eine Alternative, wenn Sie nicht reparieren müssen, gibt es die Bibliothek xmlpp.py mit der Funktion get_pprint(). Es funktionierte gut und reibungslos für meine Anwendungsfälle, ohne dass ein lxml ElementTree-Objekt verarbeitet werden musste.

1
gaborous

Ich hatte dieses Problem und löste es so:

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)

In meinem Code wird diese Methode folgendermaßen aufgerufen:

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!")

Dies funktioniert nur, weil etree standardmäßig two spaces zum Einrücken verwendet, was ich nicht sehr hervorhebt, um den Einzug zu betonen, und daher nicht hübsch. Ich konnte keine Einstellung für Etree oder Parameter für eine Funktion zum Ändern des Standardeinrückens von Etree festlegen. Ich mag es, wie einfach es ist, etree zu benutzen, aber das hat mich wirklich gestört.

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='    ',))

Es funktioniert gut für die XML mit Chinesisch!

0
Reed_Xia

Zum Konvertieren eines gesamten XML-Dokuments in ein hübsches XML-Dokument
(Bsp. vorausgesetzt, Sie haben eine .odt- oder .ods-Datei von LibreOffice Writer entpackt und möchten die hässliche "content.xml" -Datei in eine hübsche für automatisierte git-Versionskontrolle konvertieren). und git difftooling von .odt/.ods-Dateien, z. B. verwende ich hier )

import xml.dom.minidom

file = open("./content.xml", 'r')
xml_string = file.read()
file.close()

parsed_xml = xml.dom.minidom.parseString(xml_string)
pretty_xml_as_string = parsed_xml.toprettyxml()

file = open("./content_new.xml", 'w')
file.write(pretty_xml_as_string)
file.close()

Verweise:
- Danke an Ben Nolands Antwort auf dieser Seite , das mich am meisten dort hingebracht hat.

0
Gabriel Staples