XML красивая печать не работает в Python lxml

Я пытаюсь читать, изменять и писать файл XML с помощью lxml 4.1.1 в Python 2.7.6.

Мой код:

import lxml.etree as et

fn_xml_in = 'in.xml'
parser = et.XMLParser(remove_blank_text=True)
xml_doc = et.parse(fn_xml_in, parser)
xml_doc.getroot().find('b').append(et.Element('c'))
xml_doc.write('out.xml', method='html', pretty_print=True)

Входной файл in.xml выглядит так:

<a>
    <b/>
</a>

И созданный выходной файл out.xml:

<a>
    <b><c></c></b>
</a>

Или когда я устанавливаю remove_blank_text=True:

<a><b><c></c></b></a>

Я ожидал, что lxml будет вставлять разрывы строк и отступы внутри элемента b:

<a>
    <b>
        <c></c>
    </b>
</a>

Как я могу этого добиться?

Я пробовал некоторые оболочки tidy lib, но они, похоже, специализируются на HTML, а не на XML.

Я также пытался добавить символы новой строки как b tail, но тогда даже отступ нарушается.

Изменить: мне нужно, чтобы элемент c оставался разделенным в открывающем и закрывающем теге: <c></c>. Вот почему я использую method='HTML' в примере.


person Ingo Schalk-Schupp    schedule 13.12.2017    source источник
comment
Удалите method='html' или используйте method='xml'.   -  person mzjn    schedule 13.12.2017
comment
Спасибо, это указало мне на правильное решение!   -  person Ingo Schalk-Schupp    schedule 14.12.2017


Ответы (2)


Используйте метод вывода "xml" при записи (это значение по умолчанию, поэтому его не нужно указывать явно).

Задайте для свойства text элемента c пустую строку, чтобы обеспечить сериализацию элемента как <c></c>.

Код:

import lxml.etree as et

parser = et.XMLParser(remove_blank_text=True)
xml_doc = et.parse('in.xml', parser)

b = xml_doc.getroot().find('b')
c = et.Element('c')
c.text=''
b.append(c)

xml_doc.write('out.xml', pretty_print=True)

Результат (out.xml):

<a>
  <b>
    <c></c>
  </b>
</a>
person mzjn    schedule 14.12.2017

Благодаря комментарию mzjn я нашел рабочее, но не элегантное решение. Поскольку мне нужно, чтобы пустые элементы оставались в синтаксисе HTML, простое использование method='XML' не удовлетворяет.

Форматирование документа дважды дает желаемый результат:

import lxml.etree as et

parser = et.XMLParser(remove_blank_text=True)
xml_doc = et.parse('in.xml', parser)
xml_doc.getroot().find('b').append(et.Element('c'))
xml_doc.write('out.xml', pretty_print=True)

parser = et.XMLParser(remove_blank_text=False)
xml_doc = et.parse('out.xml', parser)
xml_doc.write('out.xml', pretty_print=True, method='HTML')

приводит к:

<a>
  <b>
    <c></c>
  </b>
</a>

Не изящно, но работает.

person Ingo Schalk-Schupp    schedule 14.12.2017
comment
Вместо того, чтобы анализировать документ дважды, вы можете установить для свойства text элемента c пустую строку. Это гарантирует, что элемент будет сериализован как <c></c>. См. stackoverflow.com/a/19548368/407651. - person mzjn; 14.12.2017
comment
Замечательно! Разница между пустой строкой и вообще ничем. Не могли бы вы превратить ваши комментарии в ответ? Я скорее приму твое, чем свое. - person Ingo Schalk-Schupp; 14.12.2017