Respostas:
import xml.dom.minidom
dom = xml.dom.minidom.parse(xml_fname) # or xml.dom.minidom.parseString(xml_string)
pretty_xml_as_string = dom.toprettyxml()
O lxml é recente, atualizado e inclui uma bonita função de impressão
import lxml.etree as etree
x = etree.parse("filename")
print etree.tostring(x, pretty_print=True)
Confira o tutorial do lxml: http://lxml.de/tutorial.html
aptitude install
fora. No OS / X, não tenho certeza.
print(etree.tostring(x, pretty_print=True, encoding="unicode"))
. A gravação em um arquivo de saída é possível em apenas uma linha, não sendo necessária nenhuma variável intermediária:etree.parse("filename").write("outputfile", encoding="utf-8")
Outra solução é pegar emprestada essa indent
função , para uso com a biblioteca ElementTree incorporada ao Python desde a versão 2.5. Aqui está o que isso seria:
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])
escrever para o arquivo ( tree
sendo a instância do ElementTree).
tree = ElementTree.parse('file) ; root = tree.getroot() ; indent(root); tree.write('Out.xml');
Aqui está minha solução (hacky?) Para solucionar o problema feio do nó de texto.
uglyXml = doc.toprettyxml(indent=' ')
text_re = re.compile('>\n\s+([^<>\s].*?)\n\s+</', re.DOTALL)
prettyXml = text_re.sub('>\g<1></', uglyXml)
print prettyXml
O código acima produzirá:
<?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>
Em vez disso:
<?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>
Isenção de responsabilidade: Provavelmente existem algumas limitações.
re.compile
antes da sub
operação (eu estava usando re.findall()
duas vezes, zip
e um for
laço com str.replace()
...)
Como outros apontaram, o lxml possui uma bonita impressora embutida.
Esteja ciente de que, por padrão, ele altera as seções CDATA para texto normal, o que pode ter resultados desagradáveis.
Aqui está uma função Python que preserva o arquivo de entrada e altera apenas o recuo (observe o strip_cdata=False
). Além disso, garante que a saída use UTF-8 como codificação em vez do ASCII padrão (observe o 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')
Exemplo de uso:
prettyPrintXml('some_folder/some_file.xml')
BeautifulSoup tem um prettify()
método fácil de usar .
Recua um espaço por nível de recuo. Funciona muito melhor que o pretty_print do lxml e é curto e agradável.
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 você tiver, xmllint
pode gerar um subprocesso e usá-lo. xmllint --format <file>
imprime bastante seu XML de entrada na saída padrão.
Observe que esse método usa um programa externo ao python, o que o torna uma espécie 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))
Tentei editar a resposta de "ade" acima, mas o Stack Overflow não me permitiu editar depois que eu havia inicialmente fornecido o feedback anonimamente. Esta é uma versão menos complicada da função para imprimir um ElementTree com bastante facilidade.
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 você estiver usando uma implementação DOM, cada um terá sua própria forma de impressão bonita embutida:
# 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 você estiver usando outra coisa sem a sua própria impressora bonita - ou essas impressoras bonitas não fizerem exatamente da maneira que você deseja - você provavelmente terá que escrever ou subclassificar seu próprio serializador.
Eu tive alguns problemas com as bonitas impressões do minidom. Eu recebia um UnicodeError sempre que tentava imprimir um documento com caracteres fora da codificação especificada, por exemplo, se eu tivesse um β em um documento e tentasse doc.toprettyxml(encoding='latin-1')
. Aqui está minha solução alternativa para isso:
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)
Ele não adicionará espaços ou novas linhas dentro de nós de texto, a menos que você solicite com:
indent(mystring, indent_text = True)
Você pode especificar como deve ser a unidade de indentação e como deve ser a nova linha.
pretty_xml_string = indent(
ugly_xml_string,
indentation = ' ',
newline = '\r\n'
)
O documento está na página inicial http://www.yattag.org .
Eu escrevi uma solução para percorrer um ElementTree existente e usar texto / cauda para identá-lo 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
A impressão bonita de XML para python parece muito boa para esta tarefa. (Também chamado apropriadamente.)
Uma alternativa é usar pyXML , que possui uma função PrettyPrint .
HTTPError: 404 Client Error: Not Found for url: https://pypi.org/simple/xmlpp/
Pense que o projeto está no sótão hoje em dia, que vergonha.
Você pode usar a biblioteca externa popular xmltodict , com unparse
e pretty=True
obterá o melhor resultado:
xmltodict.unparse(
xmltodict.parse(my_xml), full_document=False, pretty=True)
full_document=False
contra <?xml version="1.0" encoding="UTF-8"?>
no topo.
Aqui está uma solução Python3 que elimina o feio problema de nova linha (toneladas de espaço em branco) e usa apenas bibliotecas padrão, diferente da maioria das outras implementações.
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)
Eu descobri como corrigir o problema comum de nova linha aqui .
Dê uma olhada no módulo vkbeautify .
É uma versão python do meu popular plugin javascript / nodejs com o mesmo nome. Pode imprimir / reduzir bastante o texto XML, JSON e CSS. Entrada e saída podem ser string / arquivo em qualquer combinação. É muito compacto e não tem nenhuma dependência.
Exemplos :
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')
Uma alternativa, se você não quiser revisar novamente , existe a biblioteca xmlpp.py com a get_pprint()
função Funcionou bem e sem problemas nos meus casos de uso, sem ter que revisar novamente para um objeto lxml ElementTree.
Você pode tentar esta variação ...
Instale BeautifulSoup
e as lxml
bibliotecas de back-end (analisador):
user$ pip3 install lxml bs4
Processe seu 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 o analisador HTML do lxml - consulte os documentos do BS4 . Você precisa 'xml'
ou 'lxml-xml'
para o analisador XML.
lxml’s XML parser BeautifulSoup(markup, "lxml-xml") BeautifulSoup(markup, "xml")
lxml-xml
) e, em seguida, passou a votar no mesmo dia. Enviei uma reclamação oficial à S / O, mas eles se recusaram a investigar. De qualquer forma, desde então "adulterei" minha resposta, que agora está correta novamente (e especifica lxml-xml
como originalmente). Obrigado.
Eu tive esse problema e resolvi-o assim:
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)
No meu código, esse método é chamado assim:
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!")
Isso funciona apenas porque etree, por padrão, usa two spaces
indentação, o que não acho muito enfatizar a indentação e, portanto, não é bonito. Não consegui identificar nenhuma configuração para etree ou parâmetro para qualquer função para alterar o recuo etree padrão. Eu gosto de como é fácil usar etree, mas isso foi realmente me irritando.
Para converter um documento xml inteiro em um documento xml bonito
(por exemplo: supondo que você tenha extraído [descompactado] um arquivo .odt ou .ods do LibreOffice Writer e que você deseje converter o arquivo "content.xml" feio em um arquivo bonito para controle de versão git automatizado e git difftool
ing de arquivos .odt / .ods , como eu estou implementando aqui )
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()
Referências:
- Graças à resposta de Ben Noland nesta página, que me levou a maior parte do caminho até lá.
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=' ',))
Está funcionando bem para o xml com chinês!
Se, por algum motivo, você não conseguir colocar em mãos nenhum dos módulos Python mencionados por outros usuários, sugiro a seguinte solução para o 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)
Até onde eu sei, esta solução funcionará em sistemas baseados em Unix que possuem o xmllint
pacote instalado.
check_output
porque você não precisa fazer a verificação de erros
Resolvi isso com algumas linhas de código, abrindo o arquivo, passando por ele e adicionando recuo e salvando-o novamente. Eu estava trabalhando com pequenos arquivos xml e não queria adicionar dependências ou mais bibliotecas para instalar para o usuário. Enfim, aqui está o que eu acabei com:
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 mim, talvez alguém tenha alguma utilidade :)