leer - xml python 3
Bonita impresiĆ³n de XML en Python (19)
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+</'', 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.
¿Cuál es la mejor manera (o incluso las varias) de imprimir bonitos xml en Python?
BeautifulSoup tiene una función 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.
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))
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 codificación 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'')
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 / minificar 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'')
Escribí una solución para recorrer un ElementTree existente y usar texto / cola 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
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. Así es como se vería eso:
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)
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 <?xml version="1.0" encoding="UTF-8"?>
en la parte superior.
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<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()
A mí me funciona, tal vez alguien lo use :)
Si está usando 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.
Si tiene xmllint
, puede generar un subproceso y usarlo. xmllint --format <file>
imprime bastante su entrada XML en 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.
from bs4 import BeautifulSoup
bs = BeautifulSoup(open(xml_file), ''xml'')
print bs.prettify()
Traté de editar la respuesta de "ade" anteriormente, pero 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 += '' ''
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 codificación 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''<?xml version="1.0" ?>'',
u''<?xml version="1.0" encoding="%s"?>'' % encoding)
return unistr.encode(encoding, ''xmlcharrefreplace'')
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
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 que una función cambie la sangría estándar de etree. Me gusta lo fácil que es usar etree, pero esto me molestó mucho.
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.
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
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 .
Para convertir un documento XML completo en un documento XML bonito
(p. ej .: suponiendo que ha extraído [descomprimido] un archivo .odt o .ods de LibreOffice Writer, y desea convertir el archivo "content.xml" feo en uno bonito para el control de versiones automatizado de git y la git difftool
de datos de .odt /.ods archivos , como estoy implementando here )
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í.
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 .
import xml.dom.minidom
xml = xml.dom.minidom.parse(xml_fname) # or xml.dom.minidom.parseString(xml_string)
pretty_xml_as_string = xml.toprettyxml()