Красивая печать XML на Python - PullRequest
378 голосов
/ 15 апреля 2009

Каков наилучший (или даже различные) способ печатать xml в Python?

Ответы [ 19 ]

343 голосов
/ 30 июля 2009
import xml.dom.minidom

dom = xml.dom.minidom.parse(xml_fname) # or xml.dom.minidom.parseString(xml_string)
pretty_xml_as_string = dom.toprettyxml()
144 голосов
/ 15 апреля 2009

lxml недавно обновлен и включает в себя симпатичную функцию печати

import lxml.etree as etree

x = etree.parse("filename")
print etree.tostring(x, pretty_print=True)

Ознакомьтесь с руководством по lxml: http://lxml.de/tutorial.html

99 голосов
/ 04 января 2011

Другим решением является заимствование этой indent функции для использования с библиотекой ElementTree, которая встроена в Python с версии 2.5. Вот как это будет выглядеть:

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)
46 голосов
/ 30 июля 2010

Вот мое (хакерское?) Решение, позволяющее обойти проблему уродливого текстового узла.

uglyXml = doc.toprettyxml(indent='  ')

text_re = re.compile('>\n\s+([^<>\s].*?)\n\s+</', re.DOTALL)    
prettyXml = text_re.sub('>\g<1></', uglyXml)

print prettyXml

Приведенный выше код выдаст:

<?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>

Вместо этого:

<?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>

Отказ от ответственности: Возможно, существуют некоторые ограничения.

21 голосов
/ 13 апреля 2011

Как отмечали другие, в lxml встроен симпатичный принтер.

Имейте в виду, что по умолчанию он изменяет разделы CDATA на обычный текст, что может привести к неприятным результатам.

Вот функция Python, которая сохраняет входной файл и изменяет только отступ (обратите внимание на strip_cdata=False). Кроме того, он гарантирует, что вывод использует UTF-8 в качестве кодировки вместо ASCII по умолчанию (обратите внимание на 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')

Пример использования:

prettyPrintXml('some_folder/some_file.xml')
14 голосов
/ 14 сентября 2016

BeautifulSoup имеет простой в использовании метод prettify().

Он отступает один пробел на уровень отступа. Он работает намного лучше, чем lxml's pretty_print, и он короткий и приятный.

from bs4 import BeautifulSoup

bs = BeautifulSoup(open(xml_file), 'xml')
print bs.prettify()
11 голосов
/ 17 октября 2012

Я пытался отредактировать ответ "ade" выше, но переполнение стека не позволило мне редактировать после того, как я первоначально предоставил отзыв анонимно. Это менее глючная версия функции для красивой печати 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 += '  '
10 голосов
/ 13 апреля 2012

Если у вас есть xmllint, вы можете запустить подпроцесс и использовать его. xmllint --format <file> pretty-prints его входной XML в стандартный вывод.

Обратите внимание, что этот метод использует внешнюю по отношению к python программу, что делает его своего рода хаком.

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))
8 голосов
/ 15 апреля 2009

Если вы используете реализацию DOM, у каждой есть своя собственная форма встроенной симпатичной печати:

# 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)

Если вы используете что-то другое без собственного симпатичного принтера - или эти симпатичные принтеры делают это не совсем так, как вы хотите - вам, вероятно, придется написать или создать подкласс своего собственного сериализатора.

6 голосов
/ 15 апреля 2009

У меня были некоторые проблемы с красивой печатью минидома. Я получал UnicodeError всякий раз, когда пытался распечатать документ с символами, не входящими в заданную кодировку, например, если в документе была буква β и я пытался doc.toprettyxml(encoding='latin-1'). Вот мой обходной путь для этого:

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')
...