Довольно печать XML в Python

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

19 ответов


import xml.dom.minidom

xml = xml.dom.minidom.parse(xml_fname) # or xml.dom.minidom.parseString(xml_string)
pretty_xml_as_string = xml.toprettyxml()

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

import lxml.etree as etree

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

Проверьте учебник lxml: http://lxml.de/tutorial.html


другое решение заимствовать этой 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)

вот мой ("Сокс"?) решение обойти проблему некрасивого текстового узла.

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>

отказ от ответственности: вероятно, есть некоторые ограничения.


как указывали другие, 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')

BeautifulSoup имеет простой в использовании


Если у вас xmllint вы можете создать подпроцесс и использовать его. xmllint --format <file> pretty-печатает свой входной 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))

Я попытался отредактировать ответ " 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 += '  '

Если вы используете реализацию 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)

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


у меня были некоторые проблемы с красивой печатью minidom. Я бы получил 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')

from yattag import indent

pretty_string = indent(ugly_string)

Он не будет добавлять пробелы или новые строки внутри текстовых узлов, Если вы не попросите его с:

indent(mystring, indent_text = True)

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

pretty_xml_string = indent(
    ugly_xml_string,
    indentation = '    ',
    newline = '\r\n'
)

док включен http://www.yattag.org Домашняя страница.


XML Print Print для python выглядит довольно хорошо для этой задачи. (Имя тоже подходящее.)

альтернативой является использование pyXML, которая имеет функции PrettyPrint.


Я написал решение, чтобы пройти через существующий ElementTree и использовать text/tail для отступа, как обычно ожидается.

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

посмотри vkbeautify модуль.

это версия python моего очень популярного плагина javascript/nodejs с тем же именем. Он может довольно печатать / минимизировать XML, JSON и CSS-текст. Вход и выход могут быть string / file в любых комбинациях. Она очень компактна и не имеет никакой зависимости.

примеры:

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

вы можете использовать популярную внешнюю библиотеку xmltodict С unparse и pretty=True вы получите лучший результат:

xmltodict.unparse(
    xmltodict.parse(my_xml), full_document=False, pretty=True)

full_document=False против <?xml version="1.0" encoding="UTF-8"?> в верхней части.


альтернативой, если вы не хотите переделывать, естьxmlpp.py библиотека С . Он работал хорошо и плавно для моих случаев использования, без необходимости повторной обработки объекта lxml ElementTree.


у меня была эта проблема и я решил ее так:

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)

В моем коде этот метод называется так:

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!")

это работает только потому, что etree по умолчанию использует two spaces к отступу, который я не нахожу очень подчеркивающим отступ и, следовательно, не очень красивым. Я не мог ind никаких настроек для etree или параметра для любой функции, чтобы изменить стандартный отступ etree. Мне нравится, как легко использовать etree, но это действительно раздражало меня.


для преобразования всего xml-документа в довольно xml-документ
(пример: предполагая, что вы извлекли [распаковали] LibreOffice Writer .odt или .ODS файл, и вы хотите преобразовать уродливый " контент.xml " файл в довольно один для автоматический контроль версий git и git difftooling of .в odt/.ODS файлы, например, я ввожу здесь)

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

ссылки:
- Спасибо ответ Бена Ноланда на это страница что у меня большую часть пути там.


Я решил это с помощью некоторых строк кода, открыв файл, пройдя через него и добавив отступ, а затем сохранив его снова. Я работал с небольшими xml-файлами и не хотел добавлять зависимости или устанавливать больше библиотек для пользователя. Во всяком случае, вот что я закончил:

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

это работает для меня, возможно, кто-то еще и пригодится :)