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