Risposte:
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 è recente, aggiornato e include una bella funzione di stampa
import lxml.etree as etree
x = etree.parse("filename")
print etree.tostring(x, pretty_print=True)
Dai un'occhiata al tutorial lxml: http://lxml.de/tutorial.html
aptitude install
lontani. Sotto OS / X non ne sono sicuro.
print(etree.tostring(x, pretty_print=True, encoding="unicode"))
. La scrittura su un file di output è possibile in una sola riga, non è necessaria alcuna variabile intermedia:etree.parse("filename").write("outputfile", encoding="utf-8")
Un'altra soluzione è prendere in prestito questa indent
funzione , per l'uso con la libreria ElementTree integrata in Python dal 2.5. Ecco come sarebbe:
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])
per scrivere su file ( tree
essendo l'istanza ElementTree).
tree = ElementTree.parse('file) ; root = tree.getroot() ; indent(root); tree.write('Out.xml');
Ecco la mia (hacky?) Soluzione per aggirare il brutto problema del nodo di testo.
uglyXml = doc.toprettyxml(indent=' ')
text_re = re.compile('>\n\s+([^<>\s].*?)\n\s+</', re.DOTALL)
prettyXml = text_re.sub('>\g<1></', uglyXml)
print prettyXml
Il codice sopra riportato produrrà:
<?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>
Invece di questo:
<?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>
Disclaimer: ci sono probabilmente alcune limitazioni.
re.compile
prima sub
dell'operazione (stavo usando re.findall()
due volte, zip
e un for
ciclo con str.replace()
...)
Come altri hanno sottolineato, lxml ha una bella stampante integrata.
Tenere presente che, per impostazione predefinita, cambia le sezioni CDATA in testo normale, che può avere risultati negativi.
Ecco una funzione Python che conserva il file di input e modifica solo il rientro (notare il strip_cdata=False
). Inoltre si assicura che l'output utilizzi UTF-8 come codifica anziché ASCII predefinito (si noti che 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')
Esempio di utilizzo:
prettyPrintXml('some_folder/some_file.xml')
BeautifulSoup ha un prettify()
metodo facile da usare .
Indenta uno spazio per livello di rientro. Funziona molto meglio di pretty_print di lxml ed è breve e dolce.
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?
Se hai xmllint
, puoi generare un sottoprocesso e usarlo. xmllint --format <file>
stampa piuttosto il suo XML di input sull'output standard.
Nota che questo metodo usa un programma esterno a Python, che lo rende una specie di 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))
Ho provato a modificare la risposta "ade" sopra, ma Stack Overflow non mi ha permesso di modificarlo dopo aver inizialmente fornito un feedback anonimo. Questa è una versione meno buggy della funzione per stampare graziosamente 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 += ' '
Se stai usando un'implementazione DOM, ognuno ha la sua forma di stampa piuttosto carina integrata:
# 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)
Se stai usando qualcos'altro senza la sua graziosa stampante - o quelle graziose stampanti non lo fanno esattamente come desideri - probabilmente dovresti scrivere o sottoclassare il tuo proprio serializzatore.
Ho avuto dei problemi con la bella stampa di minidom. Otterrei un UnicodeError ogni volta che provassi a stampare un documento con caratteri al di fuori della codifica specificata, ad esempio se avessi un β in un documento e provassi doc.toprettyxml(encoding='latin-1')
. Ecco la mia soluzione alternativa per questo:
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)
Non aggiungerà spazi o nuove righe all'interno dei nodi di testo, a meno che non lo richiediate con:
indent(mystring, indent_text = True)
È possibile specificare quale dovrebbe essere l'unità di rientro e come dovrebbe apparire la newline.
pretty_xml_string = indent(
ugly_xml_string,
indentation = ' ',
newline = '\r\n'
)
Il documento è sulla homepage di http://www.yattag.org .
Ho scritto una soluzione per percorrere un ElementTree esistente e usare text / tail per rientrare come ci si aspetta in genere.
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
XML pretty print per python sembra abbastanza buono per questo compito. (Anche appropriatamente chiamato.)
Un'alternativa è usare pyXML , che ha una funzione PrettyPrint .
HTTPError: 404 Client Error: Not Found for url: https://pypi.org/simple/xmlpp/
Pensi che quel progetto sia in soffitta al giorno d'oggi, peccato.
Puoi usare la popolare libreria esterna xmltodict , con unparse
e pretty=True
otterrai i migliori risultati:
xmltodict.unparse(
xmltodict.parse(my_xml), full_document=False, pretty=True)
full_document=False
contro <?xml version="1.0" encoding="UTF-8"?>
in alto.
Ecco una soluzione Python3 che elimina il brutto problema di newline (tonnellate di spazi bianchi) e utilizza solo librerie standard a differenza della maggior parte delle altre implementazioni.
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)
Ho trovato come risolvere il problema comune di newline qui .
Dai un'occhiata al modulo vkbeautify .
È una versione Python del mio molto popolare plugin javascript / nodejs con lo stesso nome. Può piuttosto stampare / minimizzare testo XML, JSON e CSS. L'input e l'output possono essere string / file in qualsiasi combinazione. È molto compatto e non ha alcuna dipendenza.
Esempi :
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')
Un'alternativa se non si desidera ripassare , c'è la libreria xmlpp.py con la get_pprint()
funzione. Ha funzionato bene e senza intoppi per i miei casi d'uso, senza dover replicare su un oggetto ElementTree lxml.
Puoi provare questa variante ...
Librerie di installazione BeautifulSoup
e back-end lxml
(parser):
user$ pip3 install lxml bs4
Elabora il tuo 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'
usa il parser HTML di lxml - vedi i documenti BS4 . È necessario 'xml'
o 'lxml-xml'
per il parser XML.
lxml’s XML parser BeautifulSoup(markup, "lxml-xml") BeautifulSoup(markup, "xml")
lxml-xml
), e poi ha proceduto a ridimensionarlo lo stesso giorno. Ho presentato un reclamo ufficiale a S / O ma si sono rifiutati di indagare. Ad ogni modo, da allora "ho manomesso" la mia risposta, che ora è di nuovo corretta (e specifica lxml-xml
come originariamente). Grazie.
Ho avuto questo problema e l'ho risolto in questo modo:
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)
Nel mio codice questo metodo si chiama così:
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!")
Questo funziona solo perché etree di default usa il two spaces
rientro, che non trovo molto enfatizzando il rientro e quindi non carino. Non sono riuscito a indurre alcuna impostazione per etree o parametro per qualsiasi funzione per modificare il rientro etree standard. Mi piace quanto sia facile usare etree, ma questo mi ha davvero infastidito.
Per convertire un intero documento XML in un grazioso documento XML
(es: supponendo che tu abbia estratto [decompresso] un file .odt o .ods di LibreOffice Writer e desideri convertire il brutto file "content.xml" in un grazioso file per controllo automatico della versione git e git difftool
ing di file .odt / .ods , come ad esempio sto implementando qui )
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()
Riferimenti:
- Grazie alla risposta di Ben Noland in questa pagina, che mi ha permesso di arrivare fino in fondo.
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=' ',))
Funziona bene per l'xml con il cinese!
Se per qualche motivo non riesci a mettere le mani su nessuno dei moduli Python citati da altri utenti, ti suggerisco la seguente soluzione per 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)
Per quanto ne so, questa soluzione funzionerà su sistemi basati su Unix su cui è xmllint
installato il pacchetto.
check_output
perché non è necessario eseguire il controllo degli errori
Ho risolto questo problema con alcune righe di codice, aprendo il file, analizzandolo e aggiungendo rientro, quindi salvandolo di nuovo. Stavo lavorando con piccoli file XML e non volevo aggiungere dipendenze o più librerie da installare per l'utente. Comunque, ecco cosa ho finito 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()
Funziona per me, forse qualcuno ne avrà un uso :)