Как можно (вручную) редактировать XML-формат дерева элементов - PullRequest
0 голосов
/ 20 марта 2019

Допустим, у меня есть 2 XML-файла, например:

version1.xml:

<object>
    <name>boat</name>
    <pose>Unspecified</pose>
    <truncated>0</truncated>
    <difficult>0</difficult>
    <bndbox>
        <xmin>0</xmin>
        <ymin>434</ymin>
        <xmax>152</xmax>
        <ymax>504</ymax>
    </bndbox>
</object>

version2.xml:

<object><name>boat</name><pose>Unspecified</pose><truncated>0</truncated><difficult>0</difficult><bndbox><xmin>0</xmin><ymin>434</ymin><xmax>152</xmax><ymax>504</ymax></bndbox></object>

единственное различие между ними - это пробелы, которые делают первое более читабельным человеком. Я пытаюсь найти способ преобразовать второй в первый формат.

Я знаю, что могу использовать некоторую работу, например, использовать эту функцию из этого GitHub gist (и у меня есть и в других случаях):

from xml.dom import minidom
from xml.etree import ElementTree


def prettify(elem):
    """
    Return a pretty-printed XML string for the Element.
    """
    rough_string = ElementTree.tostring(elem, 'utf-8')
    reparsed = minidom.parseString(rough_string)
    return reparsed.toprettyxml(indent='  ')

но я не ищу этого (мои потребности включают частично отформатированный XML, который не работает безупречно с вышеуказанной функцией).

Если я проверяю разницу в тексте каждого элемента, я получаю следующее:

import xml.etree.ElementTree as ET

xml_path1 = 'path/to/version1.xml'
xml_path2 = 'path/to/version2.xml'
tree1 = ET.parse(xml_path1)
annot1 = tree1.getroot()
tree2 = ET.parse(xml_path2)
annot2 = tree2.getroot()

for elem1, elem2 in zip(annot1.iter(), annot2.iter()):
    if repr(elem1.text) != repr(elem2.text):
        print(elem1, repr(elem1.text), elem2, repr(elem2.text))

с выводом:

<Element 'object' at 0x7fb26fc2d9f8> '\n\t' <Element 'object' at 0x7fb1c4e4da48> None
<Element 'bndbox' at 0x7fb1c4e4d8b8> '\n\t\t' <Element 'bndbox' at 0x7fb1c4e4dbd8> None

Если я изменю указанный текст на соответствующий текст версии1, конечно, он изменит формат на затронутые элементы, но не на него.

ET.dump(annot2)

Выход:

<object>
    <name>boat</name><pose>Unspecified</pose><truncated>0</truncated><difficult>0</difficult><bndbox>
        <xmin>0</xmin><ymin>434</ymin><xmax>152</xmax><ymax>504</ymax></bndbox></object>

с желаемым выходным значением, конечно:

ET.dump(annot1)

Выход:

<object>
    <name>boat</name>
    <pose>Unspecified</pose>
    <truncated>0</truncated>
    <difficult>0</difficult>
    <bndbox>
        <xmin>0</xmin>
        <ymin>434</ymin>
        <xmax>152</xmax>
        <ymax>504</ymax>
    </bndbox>
</object>

Итак, в чем же дело с форматированием? Я знаю, что это на самом деле не влияет на содержимое XML (машины считают, что содержимое является таким же, как я думаю), но, когда Элементарное дерево сохраняет эту информацию, я не могу это сделать.

  • Существует ли какой-либо (сложный и, вероятно, неэффективный) способ управления формат XML?
  • И почему некоторые элементы, которые содержат действительные значения, такие как xmin, в моем случае только отображают это значение и скрывают форматирующий текст, в то время как другие, такие как bndbox, который фактически инкапсулирует другие подэлементы, отображают эти строки форматирования?

1 Ответ

1 голос
/ 20 марта 2019

Отсутствующий пробел находится в Element.tail ( документы ).

При генерации вывода ElementTree печатает начальный элемент, содержимое, конечный элемент и затем хвост.

Вот прием для форматирования поддерева (и оставьте остальную часть документа в покое):

  1. Довольно распечатайте поддерево, используя ваш трюк выше
  2. Преобразовать в строку
  3. Заменить "\n" в строке на "\n" + (" "*level), где level - глубина поддерева.
  4. Разобрать строку с ETree в документ и заменить поддерево корневым элементом.нового документа.

Кроме того, вы можете создать новый документ, обернув поддерево в level элементах-обертках, просто распечатав весь документ и затем снова найдя поддерево.

...