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


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

19 345

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)

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

у меня были некоторые проблемы с красивой печатью 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 pretty print для python выглядит довольно хорошо для этой задачи. (Имя тоже подходящее.)

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

Я написал решение, чтобы пройти через существующее дерево элементов и использовать 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 текст. Вход и выход могут быть строкой/файлом в любых комбинациях. Он очень компактен и не имеет никакой зависимости.

примеры:

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 difftoolков .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()

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