Odpowiedzi:
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 jest aktualny, zaktualizowany i zawiera ładną funkcję drukowania
import lxml.etree as etree
x = etree.parse("filename")
print etree.tostring(x, pretty_print=True)
Sprawdź samouczek lxml: http://lxml.de/tutorial.html
aptitude install
daleko. W OS / X nie jestem pewien.
print(etree.tostring(x, pretty_print=True, encoding="unicode"))
. Zapis do pliku wyjściowego jest możliwy tylko w jednym wierszu, nie jest wymagana zmienna pośrednia:etree.parse("filename").write("outputfile", encoding="utf-8")
Innym rozwiązaniem jest pożyczenie tej indent
funkcji do użytku z biblioteką ElementTree, która jest wbudowana w Python od wersji 2.5. Oto jak by to wyglądało:
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])
zapisywanie do pliku ( tree
będąc instancją ElementTree).
tree = ElementTree.parse('file) ; root = tree.getroot() ; indent(root); tree.write('Out.xml');
Oto moje (hacky?) Rozwiązanie obejścia brzydkiego problemu z węzłem tekstowym.
uglyXml = doc.toprettyxml(indent=' ')
text_re = re.compile('>\n\s+([^<>\s].*?)\n\s+</', re.DOTALL)
prettyXml = text_re.sub('>\g<1></', uglyXml)
print prettyXml
Powyższy kod wygeneruje:
<?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>
Zamiast tego:
<?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>
Oświadczenie: Prawdopodobnie istnieją pewne ograniczenia.
re.compile
przed sub
operacją (użyłem re.findall()
dwa razy zip
i for
pętli z str.replace()
...)
Jak zauważyli inni, lxml ma wbudowaną ładną drukarkę.
Pamiętaj jednak, że domyślnie zmienia sekcje CDATA na normalny tekst, co może mieć nieprzyjemne wyniki.
Oto funkcja Pythona, która zachowuje plik wejściowy i zmienia tylko wcięcia (zwróć uwagę na strip_cdata=False
). Ponadto upewnia się, że dane wyjściowe wykorzystują kodowanie UTF-8 zamiast domyślnego ASCII (zwróć uwagę na 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')
Przykładowe użycie:
prettyPrintXml('some_folder/some_file.xml')
BeautifulSoup ma łatwą w użyciu prettify()
metodę.
Wcina jedno miejsce na poziom wcięcia. Działa znacznie lepiej niż ładna wersja lxml i jest krótka i słodka.
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?
Jeśli masz xmllint
, możesz odrodzić podproces i użyć go. xmllint --format <file>
pretty-wypisuje wejściowy XML na standardowe wyjście.
Zauważ, że ta metoda wykorzystuje program zewnętrzny do Pythona, co czyni z niej rodzaj włamania.
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))
Próbowałem edytować odpowiedź „ade” powyżej, ale przepełnienie stosu nie pozwoliło mi na edycję po tym, jak początkowo dostarczyłem anonimową informację zwrotną. Jest to mniej błędna wersja funkcji do wydrukowania 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 += ' '
Jeśli używasz implementacji DOM, każda z nich ma swoją własną wbudowaną formę ładnego drukowania:
# 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)
Jeśli używasz czegoś innego bez własnej ładnej drukarki - lub te ładne drukarki nie robią tego tak, jak chcesz - prawdopodobnie będziesz musiał napisać lub podklasować własny serializator.
Miałem pewne problemy z ładnym nadrukiem minidoma. Otrzymywałbym błąd Unicode za każdym razem, gdy próbowałem ładnie wydrukować dokument ze znakami spoza podanego kodowania, np. Gdybym miał β w dokumencie i próbowałem doc.toprettyxml(encoding='latin-1')
. Oto moje obejście tego:
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)
Nie doda spacji ani nowych linii w węzłach tekstowych, chyba że poprosisz o to za pomocą:
indent(mystring, indent_text = True)
Możesz określić, jaka powinna być jednostka wcięcia i jak powinna wyglądać nowa linia.
pretty_xml_string = indent(
ugly_xml_string,
indentation = ' ',
newline = '\r\n'
)
Dokument znajduje się na stronie głównej http://www.yattag.org .
Napisałem rozwiązanie, aby przejść przez istniejący ElementTree i użyć tekstu / ogona, aby wprowadzić wcięcie zgodnie z oczekiwaniami.
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
Ładny wydruk XML dla Pythona wygląda całkiem dobrze do tego zadania. (Również odpowiednio nazwany.)
Alternatywą jest użycie pyXML , który ma funkcję PrettyPrint .
HTTPError: 404 Client Error: Not Found for url: https://pypi.org/simple/xmlpp/
Pomyśl, że ten projekt jest obecnie na poddaszu, wstyd.
Oto rozwiązanie Python3, które pozbywa się brzydkiego problemu nowej linii (tony białych znaków) i wykorzystuje tylko standardowe biblioteki, w przeciwieństwie do większości innych implementacji.
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)
Tutaj dowiedziałem się, jak rozwiązać typowy problem nowej linii .
Możesz użyć popularnej biblioteki zewnętrznej xmltodict , unparse
a pretty=True
uzyskasz najlepszy wynik:
xmltodict.unparse(
xmltodict.parse(my_xml), full_document=False, pretty=True)
full_document=False
przeciwko <?xml version="1.0" encoding="UTF-8"?>
na górze.
Spójrz na moduł vkbeautify .
Jest to wersja Pythona mojej bardzo popularnej wtyczki javascript / nodejs o tej samej nazwie. Może ładnie drukować / zmniejszać tekst XML, JSON i CSS. Dane wejściowe i wyjściowe mogą być ciągiem / plikiem w dowolnej kombinacji. Jest bardzo kompaktowy i nie ma żadnej zależności.
Przykłady :
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')
Alternatywą, jeśli nie chcesz przeprowadzać ponownej analizy, jest biblioteka xmlpp.py z get_pprint()
funkcją. Działa ładnie i płynnie w moich przypadkach użycia, bez konieczności ponownej analizy obiektu lxml ElementTree.
Możesz wypróbować tę odmianę ...
Zainstaluj BeautifulSoup
i lxml
biblioteki zaplecza (analizatora składni):
user$ pip3 install lxml bs4
Przetwarzaj dokument 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'
używa parsera HTML lxml - patrz dokumentacja BS4 . Potrzebujesz 'xml'
lub 'lxml-xml'
do analizatora składni XML.
lxml’s XML parser BeautifulSoup(markup, "lxml-xml") BeautifulSoup(markup, "xml")
lxml-xml
), a następnie przystąpił do głosowania tego samego dnia. Złożyłem oficjalną skargę do S / O, ale odmówili przeprowadzenia dochodzenia. Zresztą od tamtej pory „zmanipulowałem” moją odpowiedź, która teraz jest znowu poprawna (i określa, lxml-xml
jak pierwotnie). Dziękuję Ci.
Miałem ten problem i rozwiązałem go w następujący sposób:
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)
W moim kodzie ta metoda nazywa się tak:
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!")
Działa to tylko dlatego, że etree domyślnie używa two spaces
wcięć, które nie bardzo podkreślają wcięcia i dlatego nie są ładne. Nie mogłem określić żadnego ustawienia etree ani parametru dla dowolnej funkcji, aby zmienić standardowe wcięcie etree. Lubię, jak łatwo jest używać etree, ale to mnie naprawdę denerwowało.
Do konwersji całego dokumentu xml do ładnego dokumentu xml
(np. Zakładając, że rozpakowałeś [rozpakowany] plik .odt lub .ods LibreOffice Writer i chcesz przekonwertować brzydki plik „content.xml” na ładny dla zautomatyzowana kontrola wersji git i git difftool
ing plików .odt / .ods , takich jak ja tutaj wdrażam )
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()
Odniesienia:
- Dzięki odpowiedzi Bena Nolanda na tej stronie, która zapewniła mi najwięcej możliwości.
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=' ',))
Działa dobrze dla xml z chińskim!
Jeśli z jakiegoś powodu nie możesz zdobyć żadnego z modułów Pythona, o których wspominali inni użytkownicy, sugeruję następujące rozwiązanie dla 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)
O ile wiem, to rozwiązanie będzie działać na systemach opartych na Uniksie, które mają xmllint
zainstalowany pakiet.
check_output
ponieważ nie trzeba sprawdzać błędów
Rozwiązałem to za pomocą kilku wierszy kodu, otwierając plik, przeglądając go i dodając wcięcia, a następnie zapisując go ponownie. Pracowałem z małymi plikami xml i nie chciałem dodawać zależności ani więcej bibliotek do zainstalowania dla użytkownika. W każdym razie oto, z czym skończyłem:
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()
To działa dla mnie, być może ktoś z niej skorzysta :)