Réponses:
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 est récent, mis à jour et inclut une jolie fonction d'impression
import lxml.etree as etree
x = etree.parse("filename")
print etree.tostring(x, pretty_print=True)
Consultez le didacticiel lxml: http://lxml.de/tutorial.html
aptitude install
loin. Sous OS / X, je ne suis pas sûr.
print(etree.tostring(x, pretty_print=True, encoding="unicode"))
. L'écriture dans un fichier de sortie est possible en une seule ligne, aucune variable intermédiaire nécessaire:etree.parse("filename").write("outputfile", encoding="utf-8")
Une autre solution consiste à emprunter cette indent
fonction , pour une utilisation avec la bibliothèque ElementTree intégrée à Python depuis la 2.5. Voici à quoi cela ressemblerait:
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])
pour écrire dans un fichier ( tree
étant l'instance ElementTree).
tree = ElementTree.parse('file) ; root = tree.getroot() ; indent(root); tree.write('Out.xml');
Voici ma solution (hacky?) Pour contourner le vilain problème de nœud de texte.
uglyXml = doc.toprettyxml(indent=' ')
text_re = re.compile('>\n\s+([^<>\s].*?)\n\s+</', re.DOTALL)
prettyXml = text_re.sub('>\g<1></', uglyXml)
print prettyXml
Le code ci-dessus produira:
<?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>
Au lieu de cela:
<?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>
Avis de non-responsabilité: il y a probablement des limitations.
re.compile
avant l' sub
opération (j'utilisais re.findall()
deux fois, zip
et une for
boucle avec str.replace()
...)
Comme d'autres l'ont souligné, lxml a une jolie imprimante intégrée.
Sachez cependant que par défaut, il modifie les sections CDATA en texte normal, ce qui peut avoir des résultats désagréables.
Voici une fonction Python qui préserve le fichier d'entrée et ne change que l'indentation (remarquez le strip_cdata=False
). De plus, il s'assure que la sortie utilise UTF-8 comme encodage au lieu de l'ASCII par défaut (notez le 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')
Exemple d'utilisation:
prettyPrintXml('some_folder/some_file.xml')
BeautifulSoup a une prettify()
méthode facile à utiliser .
Il indente un espace par niveau d'indentation. Il fonctionne beaucoup mieux que pretty_print de lxml et est court et doux.
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 vous en avez, xmllint
vous pouvez générer un sous-processus et l'utiliser.xmllint --format <file>
joli-imprime son XML d'entrée sur la sortie standard.
Notez que cette méthode utilise un programme externe à python, ce qui en fait une sorte 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))
J'ai essayé de modifier la réponse de "ade" ci-dessus, mais Stack Overflow ne m'a pas laissé modifier après avoir initialement fourni des commentaires de manière anonyme. Il s'agit d'une version moins boguée de la fonction pour joliment imprimer 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 vous utilisez une implémentation DOM, chacun a sa propre forme de jolie impression intégrée:
# 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 vous utilisez autre chose sans sa propre jolie imprimante - ou si ces jolies imprimantes ne le font pas exactement comme vous le souhaitez - vous devrez probablement écrire ou sous-classer votre propre sérialiseur.
J'ai eu quelques problèmes avec la jolie impression du minidom. J'obtiendrais un UnicodeError chaque fois que j'essayais de joliment imprimer un document avec des caractères en dehors du codage donné, par exemple si j'avais un β dans un document et que j'essayais doc.toprettyxml(encoding='latin-1')
. Voici ma solution:
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)
Il n'ajoutera pas d'espaces ou de nouvelles lignes à l'intérieur des nœuds de texte, sauf si vous le demandez avec:
indent(mystring, indent_text = True)
Vous pouvez spécifier ce que l'unité d'indentation doit être et à quoi la nouvelle ligne doit ressembler.
pretty_xml_string = indent(
ugly_xml_string,
indentation = ' ',
newline = '\r\n'
)
Le doc est sur la page d' accueil http://www.yattag.org .
J'ai écrit une solution pour parcourir un ElementTree existant et utiliser text / tail pour le mettre en retrait comme on s'y attend généralement.
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 jolie impression XML pour python semble assez bonne pour cette tâche. (Bien nommé aussi.)
Une alternative consiste à utiliser pyXML , qui a une fonction PrettyPrint .
HTTPError: 404 Client Error: Not Found for url: https://pypi.org/simple/xmlpp/
Pensez que le projet est dans le grenier de nos jours, dommage.
Vous pouvez utiliser la bibliothèque externe populaire xmltodict , avec unparse
et pretty=True
vous obtiendrez le meilleur résultat:
xmltodict.unparse(
xmltodict.parse(my_xml), full_document=False, pretty=True)
full_document=False
contre <?xml version="1.0" encoding="UTF-8"?>
en haut.
Voici une solution Python3 qui se débarrasse du vilain problème de nouvelle ligne (des tonnes d'espaces), et il utilise uniquement des bibliothèques standard contrairement à la plupart des autres implémentations.
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)
J'ai trouvé comment résoudre le problème de saut de ligne courant ici .
Jetez un oeil à la vkbeautify module .
C'est une version python de mon plugin javascript / nodejs très populaire avec le même nom. Il peut joliment imprimer / réduire le texte XML, JSON et CSS. L'entrée et la sortie peuvent être des chaînes / fichiers dans n'importe quelle combinaison. Il est très compact et n'a aucune dépendance.
Exemples :
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')
Une alternative si vous ne voulez pas avoir à refaire l'analyse, il y a la bibliothèque xmlpp.py avec la get_pprint()
fonction. Cela a fonctionné bien et en douceur pour mes cas d'utilisation, sans avoir à analyser à nouveau un objet ElementTree lxml.
Vous pouvez essayer cette variante ...
Installation BeautifulSoup
et lxml
bibliothèques backend (analyseur):
user$ pip3 install lxml bs4
Traitez votre document 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'
utilise l' analyseur HTML de lxml - voir la documentation BS4 . Vous avez besoin de 'xml'
ou 'lxml-xml'
pour l'analyseur XML.
lxml’s XML parser BeautifulSoup(markup, "lxml-xml") BeautifulSoup(markup, "xml")
lxml-xml
), puis ils ont procédé à downvote ce même jour. J'ai déposé une plainte officielle auprès de S / O, mais ils ont refusé d'enquêter. Quoi qu'il en soit, j'ai depuis "dé-falsifié" ma réponse, qui est maintenant à nouveau correcte (et précise lxml-xml
comme c'était le cas à l'origine). Je vous remercie.
J'ai eu ce problème et je l'ai résolu comme ceci:
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)
Dans mon code, cette méthode est appelée comme ceci:
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!")
Cela ne fonctionne que parce que etree utilise par défaut l' two spaces
indentation, que je ne trouve pas beaucoup mettant l'accent sur l'indentation et donc pas joli. Je ne pouvais pas trouver de paramètre pour etree ou de paramètre pour n'importe quelle fonction pour changer le retrait standard etree. J'aime la facilité d'utilisation d'etree, mais cela m'a vraiment énervé.
Pour convertir un document xml entier en un joli document xml
(ex: en supposant que vous avez extrait [décompressé] un fichier LibreOffice Writer .odt ou .ods, et que vous voulez convertir le vilain fichier "content.xml" en un joli pour contrôle de version git automatisé et git difftool
ing de fichiers .odt / .ods , comme je l'implémente ici )
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()
Références:
- Merci à la réponse de Ben Noland sur cette page qui m'a le plus amené là-bas.
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=' ',))
Cela fonctionne bien pour le xml avec le chinois!
Si pour une raison quelconque, vous ne pouvez pas mettre la main sur l'un des modules Python mentionnés par d'autres utilisateurs, je suggère la solution suivante pour 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)
Pour autant que je sache, cette solution fonctionnera sur les systèmes basés sur Unix sur lesquels le xmllint
package est installé.
check_output
car vous n'avez pas besoin de vérifier les erreurs
J'ai résolu cela avec quelques lignes de code, en ouvrant le fichier, en le parcourant et en ajoutant une indentation, puis en l'enregistrant à nouveau. Je travaillais avec de petits fichiers xml et je ne voulais pas ajouter de dépendances ou plus de bibliothèques à installer pour l'utilisateur. Quoi qu'il en soit, voici ce que j'ai fini avec:
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()
Cela fonctionne pour moi, peut-être que quelqu'un l'utilisera :)