Respuestas:
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 bonita función de impresión
import lxml.etree as etree
x = etree.parse("filename")
print etree.tostring(x, pretty_print=True)
Consulte el tutorial lxml: http://lxml.de/tutorial.html
aptitude install
lejos. Bajo OS / X no estoy seguro.
print(etree.tostring(x, pretty_print=True, encoding="unicode"))
. Escribir en un archivo de salida es posible en una sola línea, no se necesita una variable intermedia:etree.parse("filename").write("outputfile", encoding="utf-8")
Otra solución es tomar prestada esta indent
función , para usar con la biblioteca ElementTree que está integrada en Python desde 2.5. Así es como se vería:
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)
tree.write([filename])
para escribir en el archivo ( tree
siendo la instancia de ElementTree).
tree = ElementTree.parse('file) ; root = tree.getroot() ; indent(root); tree.write('Out.xml');
Aquí está mi solución (¿hacky?) Para solucionar el problema del nodo de texto feo.
uglyXml = doc.toprettyxml(indent=' ')
text_re = re.compile('>\n\s+([^<>\s].*?)\n\s+</', re.DOTALL)
prettyXml = text_re.sub('>\g<1></', uglyXml)
print prettyXml
El código anterior producirá:
<?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>
En lugar de esto:
<?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>
Descargo de responsabilidad: probablemente hay algunas limitaciones.
re.compile
antes de la sub
operación (la estaba usando re.findall()
dos veces zip
y un for
ciclo con str.replace()
...)
Como otros señalaron, lxml tiene una bonita impresora integrada.
Sin embargo, 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 la strip_cdata=False
). Además, se asegura de que la salida use UTF-8 como codificación en lugar del ASCII predeterminado (observe el 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 prettify()
método fácil de usar .
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()
bs4.FeatureNotFound: Couldn't find a tree builder with the features you requested: xml. Do you need to install a parser library?
Si lo tiene xmllint
, puede generar un subproceso y usarlo. xmllint --format <file>
pretty-imprime su entrada XML a la salida estándar.
Tenga en cuenta que este método utiliza un programa 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" anterior, pero Stack Overflow no me permitió editar después de haber proporcionado comentarios de forma anónima. Esta es una versión menos defectuosa de la función para imprimir bonitamente 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 bonita incorporada:
# 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á utilizando otra cosa sin su propia impresora bonita, o si esas impresoras bonitas no lo hacen de la manera deseada, probablemente tenga que escribir o subclasificar su propio serializador.
Tuve algunos problemas con la bonita impresión de minidom. Obtendía un UnicodeError cada vez que intentaba imprimir un documento con caracteres fuera de la codificación dada, por ejemplo, si tenía un β en un documento y lo intentaba 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'<?xml version="1.0" ?>',
u'<?xml version="1.0" encoding="%s"?>' % 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 cuál debería ser la unidad de sangría y cómo debería verse 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 .
Escribí una solución para recorrer un ElementTree existente y usar text / tail para sangrarlo como normalmente se espera.
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
La impresión bonita de XML para Python se ve bastante bien para esta tarea. (Apropiadamente nombrado, también.)
Una alternativa es usar pyXML , que tiene una función PrettyPrint .
HTTPError: 404 Client Error: Not Found for url: https://pypi.org/simple/xmlpp/
Creo que el proyecto está en el ático hoy en día, lástima.
Puede usar la popular biblioteca externa xmltodict , con unparse
y pretty=True
obtendrá el mejor resultado:
xmltodict.unparse(
xmltodict.parse(my_xml), full_document=False, pretty=True)
full_document=False
en contra <?xml version="1.0" encoding="UTF-8"?>
en la parte superior.
Aquí hay una solución Python3 que elimina el feo problema de la nueva línea (toneladas de espacios en blanco), y solo usa bibliotecas estándar a diferencia de la mayoría de las otras implementaciones.
import xml.etree.ElementTree as ET
import xml.dom.minidom
import os
def pretty_print_xml_given_root(root, output_xml):
"""
Useful for when you are editing xml data on the fly
"""
xml_string = xml.dom.minidom.parseString(ET.tostring(root)).toprettyxml()
xml_string = os.linesep.join([s for s in xml_string.splitlines() if s.strip()]) # remove the weird newline issue
with open(output_xml, "w") as file_out:
file_out.write(xml_string)
def pretty_print_xml_given_file(input_xml, output_xml):
"""
Useful for when you want to reformat an already existing xml file
"""
tree = ET.parse(input_xml)
root = tree.getroot()
pretty_print_xml_given_root(root, output_xml)
Encontré cómo solucionar el problema común de nueva línea aquí .
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 / minificar bastante 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 desea volver a analizar, existe la biblioteca xmlpp.py con la get_pprint()
función. Funcionó bien y sin problemas para mis casos de uso, sin tener que volver a analizarlo en un objeto lxml ElementTree.
Puedes probar esta variación ...
Instalar BeautifulSoup
y las lxml
bibliotecas de backend (analizador):
user$ pip3 install lxml bs4
Procese su documento XML:
from bs4 import BeautifulSoup
with open('/path/to/file.xml', 'r') as doc:
for line in doc:
print(BeautifulSoup(line, 'lxml-xml').prettify())
'lxml'
utiliza el analizador HTML de lxml ; consulte los documentos de BS4 . Necesita 'xml'
o 'lxml-xml'
para el analizador XML.
lxml’s XML parser BeautifulSoup(markup, "lxml-xml") BeautifulSoup(markup, "xml")
lxml-xml
), y luego procedió a votarlo ese mismo día. Envié una queja oficial a S / O pero se negaron a investigar. De todos modos, desde entonces "des-manipulé" mi respuesta, que ahora es correcta nuevamente (y especifica lxml-xml
como lo hizo originalmente). Gracias.
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('<?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!")
Esto funciona solo porque etree por defecto usa two spaces
sangría, lo que no encuentro enfatizando mucho la sangría y, por lo tanto, no es bonita. No pude encontrar ninguna configuración para etree o parámetro para ninguna función para cambiar la sangría etree estándar. 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 control automatizado de versiones git e git difftool
ing de archivos .odt / .ods , como los que 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 llevó casi todo el camino.
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=' ',))
¡Funciona bien para el xml con chino!
Si por alguna razón no puede tener en sus manos ninguno de los módulos de Python que mencionaron otros usuarios, sugiero la siguiente solución para Python 2.7:
import subprocess
def makePretty(filepath):
cmd = "xmllint --format " + filepath
prettyXML = subprocess.check_output(cmd, shell = True)
with open(filepath, "w") as outfile:
outfile.write(prettyXML)
Hasta donde sé, esta solución funcionará en sistemas basados en Unix que tengan el xmllint
paquete instalado.
check_output
porque no es necesario que verifique los errores
Resolví esto con algunas líneas de código, abriendo el archivo, revisándolo y agregando sangría, luego guardándolo nuevamente. 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<len(xml)-3):
simpleSplit = xml[i:(i+2)] == '><'
advancSplit = xml[i:(i+3)] == '></'
end = xml[i:(i+2)] == '/>'
start = xml[i] == '<'
if(advancSplit):
deepness += -1
new_xml += '\n' + indent*deepness
simpleSplit = False
deepness += -1
if(simpleSplit):
new_xml += '\n' + indent*deepness
if(start):
deepness += 1
if(end):
deepness += -1
f = open(file_name,'w')
f.write(new_xml)
f.close()
Funciona para mí, tal vez alguien lo use :)