Bonita impresión de XML en Python

¿Cuál es la mejor manera (o incluso las varias) de imprimir bonitos xml en Python?

import xml.dom.minidom dom = xml.dom.minidom.parse(xml_fname) # or xml.dom.minidom.parseString(xml_string) pretty_xml_as_string = dom.toprettyxml() 

lxml es reciente, actualizado e incluye una función de impresión bonita

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

Revisa el tutorial lxml: http://lxml.de/tutorial.html

Otra solución es tomar prestada esta función de indent , para usar con la biblioteca ElementTree que está integrada en Python desde la versión 2.5. Esto es lo que se vería así:

 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) 

Aquí está mi (¿hacky?) Solución para solucionar el problema del nodo de texto feo.

 uglyXml = doc.toprettyxml(indent=' ') text_re = re.compile('>\n\s+([^<>\s].*?)\n\s+\g<1> 

El código anterior producirá:

    1 Add Visual Studio 2005 and 2008 solution files 
We need Visual Studio 2005/2008 project files for Windows.

En lugar de esto:

     1   Add Visual Studio 2005 and 2008 solution files  
We need Visual Studio 2005/2008 project files for Windows.

Descargo de responsabilidad: Probablemente hay algunas limitaciones.

Como han señalado otros, lxml tiene una impresora bonita incorporada.

Tenga en cuenta que, de forma predeterminada, cambia las secciones CDATA a texto normal, lo que puede tener resultados desagradables.

Aquí hay una función de Python que conserva el archivo de entrada y solo cambia la sangría (observe el strip_cdata=False ). Además, se asegura de que la salida utilice UTF-8 como encoding en lugar del ASCII predeterminado (observe la 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') 

Ejemplo de uso:

 prettyPrintXml('some_folder/some_file.xml') 

BeautifulSoup tiene un método fácil de usar prettify() .

Se sangra un espacio por nivel de sangría. Funciona mucho mejor que pretty_print de lxml y es corto y dulce.

 from bs4 import BeautifulSoup bs = BeautifulSoup(open(xml_file), 'xml') print bs.prettify() 

Si tiene xmllint puede generar un subproceso y usarlo. xmllint --format imprime su entrada XML en salida estándar.

Tenga en cuenta que este método utiliza un progtwig externo a python, lo que lo convierte en una especie de hack.

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

Traté de editar la respuesta de “ade” anteriormente, pero Stack Overflow no me dejaba editar después de que inicialmente proporcioné comentarios de forma anónima. Esta es una versión con menos errores de la función para imprimir bastante 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 += ' ' 

Si está utilizando una implementación DOM, cada uno tiene su propia forma de impresión integrada:

 # 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 está usando otra cosa sin su propia impresora bonita, o esas impresoras bonitas no lo hacen como usted quiere, probablemente tenga que escribir o crear una subclase de su propio serializador.

Tuve algunos problemas con la impresión bonita de minidom. doc.toprettyxml(encoding='latin-1') cada vez que intentaba imprimir un documento con caracteres fuera de la encoding dada, por ejemplo, si tenía un β en un documento y probaba doc.toprettyxml(encoding='latin-1') . Aquí está mi solución para ello:

 def toprettyxml(doc, encoding): """Return a pretty-printed XML document in a given encoding.""" unistr = doc.toprettyxml().replace(u'', u'' % encoding) return unistr.encode(encoding, 'xmlcharrefreplace') 
 from yattag import indent pretty_string = indent(ugly_string) 

No agregará espacios o nuevas líneas dentro de los nodos de texto, a menos que lo solicite con:

 indent(mystring, indent_text = True) 

Puede especificar qué debe ser la unidad de sangría y qué aspecto debe tener la nueva línea.

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

El documento está en la página de inicio de http://www.yattag.org .

La impresión XML bonita para python se ve bastante bien para esta tarea. (Nombre apropiado, también.)

Una alternativa es usar pyXML , que tiene una función PrettyPrint .

Escribí una solución para recorrer un ElementTree existente y usar text / tail para sangrar como se espera normalmente.

 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 

Puede usar la biblioteca externa popular xmltodict , con unparse y pretty=True obtendrá el mejor resultado:

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

full_document=False contra en la parte superior.

Echa un vistazo al módulo vkbeautify .

Es una versión de python de mi muy popular complemento javascript / nodejs con el mismo nombre. Puede imprimir bastante / minimizar texto XML, JSON y CSS. La entrada y la salida pueden ser cadenas / archivos en cualquier combinación. Es muy compacto y no tiene ninguna dependencia.

Ejemplos :

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

Una alternativa si no quiere tener que volver a analizar, está la biblioteca xmlpp.py con la función get_pprint() . Funcionó bien y sin problemas para mis casos de uso, sin tener que volver a analizar un objeto ElementTree lxml.

Tuve este problema y lo resolví así:

 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) 

En mi código este método se llama así:

 try: with open(file_path, 'w') as file: file.write('') # 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!") 

Esto funciona solo porque etree por defecto usa two spaces para sangrar, lo cual no encuentro mucho enfatizando la sangría y por lo tanto no es bonito. No pude encontrar ninguna configuración para etree o parámetro para cualquier función para cambiar la sangría estándar de etree. Me gusta lo fácil que es usar etree, pero esto realmente me molestó.

Para convertir un documento XML completo en un documento XML bonito
(por ejemplo, suponiendo que ha extraído [descomprimido] un archivo .odt o .ods de LibreOffice Writer, y desea convertir el archivo feo “content.xml” en uno bonito para el control de versiones automatizado de git y la git difftool de datos de .odt /.ods archivos , como estoy implementando aquí )

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

Referencias:
– Gracias a la respuesta de Ben Noland en esta página que me ayudó a llegar hasta allí.

Resolví esto con algunas líneas de código, abriendo el archivo, revisándolo y agregando sangría, luego guardándolo de nuevo. Estaba trabajando con pequeños archivos xml y no quería agregar dependencias, o más bibliotecas para instalar para el usuario. De todos modos, esto es lo que terminé con:

  f = open(file_name,'r') xml = f.read() f.close() #Removing old indendations raw_xml = '' for line in xml: raw_xml += line xml = raw_xml new_xml = '' indent = ' ' deepness = 0 for i in range((len(xml))): new_xml += xml[i] if(i 

A mí me funciona, tal vez alguien lo use 🙂