Довольно печать 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')
Если у вас
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()
это работает для меня, возможно, кто-то еще и пригодится :)