Третье решение, о котором я почти никогда не упоминал, имеет специфику MySQL и выглядит так:
SELECT id, MAX(rev) AS rev
, 0+SUBSTRING_INDEX(GROUP_CONCAT(numeric_content ORDER BY rev DESC), ',', 1) AS numeric_content
FROM t1
GROUP BY id
Да, это выглядит ужасно (преобразование в строку и обратно и т. д.), но по моему опыту это обычно быстрее, чем другие решения. Возможно, это только для моих случаев использования, но я использовал его на таблицах с миллионами записей и множеством уникальных идентификаторов. Возможно, это связано с тем, что MySQL очень плохо оптимизирует другие решения (по крайней мере, в 5,0 дней, когда я придумал это решение).
Важно то, что GROUP_CONCAT имеет максимальную длину для строки, которую она может построить. Вероятно, вы захотите поднять этот предел, установив переменную group_concat_max_len
. И имейте в виду, что это будет предел для масштабирования, если у вас есть большое количество строк.
В любом случае вышеупомянутое не работает напрямую, если ваше поле содержимого уже является текстом. В этом случае вы, вероятно, захотите использовать другой разделитель, например, \ 0. Вы также быстрее столкнетесь с пределом group_concat_max_len
.
import xml.dom.minidom
xml = xml.dom.minidom.parse(xml_fname) # or xml.dom.minidom.parseString(xml_string)
pretty_xml_as_string = xml.toprettyxml()
Если вы используете реализацию 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)
Если вы используете что-то еще без своего собственного принтера - или эти симпатичные принтеры не совсем так делают, как вы хотите - вам, вероятно, придется писать или подклассифицировать собственный сериализатор.
BeautifulSoup имеет простую в использовании функцию prettify()
.
Отступы на один уровень отступов. Он работает намного лучше, чем lxml's pretty_print, и является коротким и сладким.
from bs4 import BeautifulSoup
bs = BeautifulSoup(open(xml_file), 'xml')
print bs.prettify()
Символьная печать XML для python выглядит довольно хорошо для этой задачи. (Соответственно названо тоже.)
Альтернативой является использование pyXML , который имеет функцию PrettyPrint .
Альтернативой, если вы не хотите переписывать, есть библиотека xmlpp.py с функцией get_pprint()
. Он работал хорошо и плавно для моих случаев использования, не переписываясь с объектом lxml ElementTree.
У меня были некоторые проблемы с красивой печатью миниода. Я бы получил 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')
Другим решением является заимствование этой функции 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)
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 .
lxml является последним, обновляется и включает в себя довольно печатную функцию
import lxml.etree as etree
x = etree.parse("filename")
print etree.tostring(x, pretty_print=True)
Проверьте учебник lxml: http://lxml.de/tutorial.html
Я написал решение, чтобы пройти через существующий 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
Вот мое решение (hacky?), чтобы обойти проблему уродливого текстового узла.
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>
Отказ от ответственности: Вероятно, существуют некоторые ограничения.
Я решил это с некоторыми строками кода, открыв файл, перейдя через него и добавив отступ, а затем снова сохранил его. Я работал с небольшими файлами 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()
Это работает для меня, возможно, кто-то будет использовать его:)
Я попытался отредактировать ответ «ade» выше, но Stack Overflow не разрешил мне редактировать после того, как я первоначально предоставил анонимную обратную связь. Это менее сложная версия функции для правильной печати 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 += ' '
Как отмечали другие, 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>
довольно-печатает свой входной 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))
Взгляните на модуль 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"?>
at верх.
У меня была эта проблема и она была решена так:
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!")
Это работает только потому, что по умолчанию использует two spaces
в отступе, который не находит особого подчеркивания отступа и, следовательно, не очень. Я не мог указывать какие-либо настройки для etree или параметра для любой функции, чтобы изменить стандартный отступ epree. Мне нравится, как легко использовать etree, но это меня действительно раздражало.