Добавление нового узла XML и красивая печать XML в python

Я могу добавить узел XML с помощью ElementTree, но это возвращает вывод в одну строку вместо древовидной структуры, похожей на то, когда я открываю файл xml в текстовом формате. Я также пытался использовать minidom.toprettyxml, но не знаю, как добавить вывод в исходный XML. Поскольку я хотел бы, чтобы сценарий воспроизводился в других средах, я предпочитаю не использовать внешние библиотеки, такие как lxml. Может кто-нибудь, пожалуйста, помогите, как я могу красиво распечатать вывод? - питон 2.7

Образец XML. Вот так это выглядит как в текстовом формате, так и в проводнике.

<?xml version="1.0" encoding="utf-8"?>
<default_locators >
  <locator_ref>
    <name>cherry</name>
    <display_name>cherrycherry</display_name>
    <workspace_properties>
      <factory_progid>Workspace</factory_progid>
      <path>InstallDir</path>
    </workspace_properties>
  </locator_ref>
</default_locators>

Ожидаемый вывод как в текстовом формате, так и в проводнике.

<?xml version="1.0" encoding="utf-8"?>
<default_locators >
  <locator_ref>
    <name>cherry</name>
    <display_name>cherrycherry</display_name>
    <workspace_properties>
      <factory_progid>Workspace</factory_progid>
      <path>InstallDir</path>
    </workspace_properties>
  </locator_ref>
  <locator_ref>
    <name>berry</name>
    <display_name>berryberry</display_name>
    <workspace_properties>
      <factory_progid>Workspace</factory_progid>
      <path>C:\temp\temp</path>
    </workspace_properties>
  </locator_ref>
</default_locators>

Мой сценарий

#coding: cp932

import xml.etree.ElementTree as ET

tree = ET.parse(r"C:\DefaultLocators.xml")
root = tree.getroot()

locator_ref = ET.SubElement(root, "locator_ref")
name = ET.SubElement(locator_ref, "name")
name.text = " berry"
display_name = ET.SubElement(locator_ref, "display_name")
display_name.text = "berryberry"
workspace_properties = ET.SubElement(locator_ref, "workspace_properties")
factory_progid = ET.SubElement(workspace_properties,"factory_progid")
factory_progid.text = "Workspace"
path = ET.SubElement(workspace_properties, "path")
path.text = r"c:\temp\temp"

tree.write(r"C:\DefaultLocators.xml", encoding='utf-8')

Возвращенный вывод. После запуска моего скрипта в мой файл sample.xml добавляются новые узлы, но он возвращает вывод в одной строке, при этом все новые строки и отступы удаляются из исходного файла sample.xml. По крайней мере, так это выглядит, когда я открываю файл sample.xml в текстовом формате. Однако, когда я открываю файл sample.xml в проводнике, он выглядит нормально. Я все еще вижу новые строки и отступы, как и раньше. Как сохранить исходную древовидную структуру в текстовом формате даже после запуска скрипта?

<default_locators>
  <locator_ref>
    <name>cherry</name>
    <display_name>cherrycherry</display_name>
    <workspace_properties>
      <factory_progid>Workspace</factory_progid>
      <path>InstallDir</path>
    </workspace_properties>
  </locator_ref>
<locator_ref><name> berry</name><display_name>berryberry</display_name><workspace_properties><factory_progid>Workspace</factory_progid><path>c:\temp\temp</path></workspace_properties></locator_ref></default_locators>

person fairyberry    schedule 09.01.2013    source источник


Ответы (3)


при работе с элементом вы можете сделать так: element.tail = '\n' тогда он будет записан в одну строку.

person UnZike    schedule 10.12.2013

напишите свой xml в elementTree как:

import xml.etree.ElementTree as ET


def serialize_xml(write, elem, encoding, qnames, namespaces):
    tag = elem.tag
    text = elem.text
    if tag is ET.Comment:
        write("<!--%s-->" % _encode(text, encoding))
    elif tag is ET.ProcessingInstruction:
        write("<?%s?>" % _encode(text, encoding))
    else:
        tag = qnames[tag]
        if tag is None:
            if text:
                write(_escape_cdata(text, encoding))
            for e in elem:
                serialize_xml(write, e, encoding, qnames, None)
        else:
            write("\n<" + tag) ## '\n' added by namit
            items = elem.items()
            if items or namespaces:
                if namespaces:
                    for v, k in sorted(namespaces.items(),
                                       key=lambda x: x[1]):  # sort on prefix
                        if k:
                            k = ":" + k
                        write(" xmlns%s=\"%s\"" % (
                            k.encode(encoding),
                            _escape_attrib(v, encoding)
                            ))
                for k, v in sorted(items):  # lexical order
                    if isinstance(k, QName):
                        k = k.text
                    if isinstance(v, QName):
                        v = qnames[v.text]
                    else:
                        v = _escape_attrib(v, encoding)
                    write(" %s=\"%s\"" % (qnames[k], v))
            if text or len(elem):
                write(">")
                if text:
                    write(ET._escape_cdata(text, encoding))
                for e in elem:
                    serialize_xml(write, e, encoding, qnames, None)
                write("</" + tag + ">")
            else:
                write(" />")
    if elem.tail:
        write(ET._escape_cdata(elem.tail, encoding))

ET._serialize_xml=serialize_xml

tree = ET.parse(r"samplexml.xml")
root = tree.getroot()

locator_ref = ET.SubElement(root, "locator_ref")
name = ET.SubElement(locator_ref, "name")
name.text = " berry"
display_name = ET.SubElement(locator_ref, "display_name")
display_name.text = "berryberry"
workspace_properties = ET.SubElement(locator_ref, "workspace_properties")
factory_progid = ET.SubElement(workspace_properties,"factory_progid")
factory_progid.text = "WorkspaceFactory"
path = ET.SubElement(workspace_properties, "path")

ins_out=open("samplexml_1.xml",'wb',1000)
ET.ElementTree(locator_ref).write(ins_out,encoding="ASCII")
ins_out.close()

это запишет полный файл в одну строку; без добавления пробела в хвост xml.

person namit    schedule 09.01.2013
comment
@ user1027101: проверьте обновленный пост; я написал полный код для этого. - person namit; 09.01.2013
comment
Спасибо за код. Я попробовал, и он работает, как вы упомянули, но знаете ли вы, как я могу вставить или добавить вывод в существующий файл XML вместо его перезаписи? - person fairyberry; 09.01.2013
comment
@ user1027101: нет; мы не можем; это файл, как и любой другой файл; это не какой-либо объект Python, который мы можем вставить или добавить; мы должны сделать новый или перезаписать существующий. - person namit; 09.01.2013
comment
Однако с моим неполным скриптом выше я мог бы добавить некоторые узлы в существующий XML-файл. См. Возвращаемый вывод в txt выше. Все, что я хочу, это иметь вывод в дереве с правильными отступами... - person fairyberry; 09.01.2013
comment
@Дж.Ф. Себастьян: не могли бы вы помочь? - person fairyberry; 09.01.2013
comment
@ user1027101: см. обновление; я добавил новую строку для каждого нового узла; но вы просите добавить больше пробелов; мы не можем указать вкладку для xml-файла; они пробелы в хвосте элемента. ‹br› Чтобы создать пробел для лучшего чтения; я вставил один при создании нового тега; в противном случае то, что запрашивается, имеет более одного пробела. - person namit; 09.01.2013
comment
давайте продолжим это обсуждение в чате - person fairyberry; 09.01.2013

Я думаю, вы должны попробовать библиотеку lxml. Это лучший способ парсить XML в Python. У него есть магический аргумент *pretty_print* для таких вещей. Вот пример:

import lxml.etree as etree

root = etree.Element("root")
for rn in range(10):
    etree.SubElement(root, "column_%s" % str(rn)).text = str(rn*rn)
pretty_data = etree.tostring(root, pretty_print=True, encoding = 'utf-8')
print final_data

Результат: http://pastebin.com/y0rkQ78G

person Skiv_mag    schedule 09.01.2013