Повторение элементов XML с использованием lxml - PullRequest
1 голос
/ 16 октября 2019

Мне нужно создать XML-документ, который должен повторять одну и ту же информацию в разных частях. Я создаю элемент details как элемент etree.Element и пытаюсь добавить его к паре вторичных элементов XML

from lxml import etree

top = etree.Element('Primary')
element1 = etree.Element('Secondary')
element2 = etree.Element('Secondary')

details = etree.Element('Details', somevalue='value')

element1.append(details)
element2.append(details)
top.append(element1)
top.append(element2)

print(etree.tostring(top, encoding="unicode", pretty_print=True))

Требуемый вывод:

<Primary>
  <Secondary>
    <Details somevalue="value"/>
  </Secondary>
  <Secondary>
    <Details somevalue="value"/>
  </Secondary>
</Primary>

вывод, который я получаю:

<Primary>
  <Secondary/>
  <Secondary>
    <Details somevalue="value"/>
  </Secondary>
</Primary>

lxml, кажется, обрабатывает элемент details, разрешенный только в одном месте. Есть ли способ отключить это?

Спасибо!

Ответы [ 2 ]

2 голосов
/ 16 октября 2019

Следующее извлечено из документации lxml

Обратите внимание, что в исходном ElementTree один объект Element может находиться в любом количестве мест в любом количестве деревьев,которая допускает ту же операцию копирования, что и со списками. Очевидным недостатком является то, что изменения такого элемента будут применяться ко всем местам, где он появляется в дереве, что может или не может быть предназначено.

Преимущество этого различия состоит в том, что элемент в lxml.etree всегдаимеет ровно одного родителя, к которому можно обратиться с помощью метода getparent (). Это не поддерживается в исходном ElementTree.

Таким образом, элемент в lxml может иметь только одного родителя в отличие от исходного элемента из ElementTree. Поэтому невозможно просто добавить один и тот же элемент к нескольким родителям с помощью lxml. Однако в документации предлагается обрабатывать этот новый дизайн, вам следует использовать глубокое копирование, чтобы скопировать элемент, который вы хотите добавить к другому элементу, если он уже где-то назначен.

Это создаст новую копию элемента и, таким образом,новая копия может быть назначена другому родительскому элементу. Это отдельные копии, поэтому изменение одного не изменит другого.

from lxml import etree
from copy import deepcopy

top = etree.Element('Primary')
element1 = etree.Element('Secondary')
element2 = etree.Element('Secondary')
details = etree.Element('Details', somevalue='value')
element1.append(details)
element2.append(deepcopy(details))
top.append(element1)
top.append(element2)
print(etree.tostring(top, encoding="unicode", pretty_print=True))

ВЫХОД

<Primary>
  <Secondary>
    <Details somevalue="value"/>
  </Secondary>
  <Secondary>
    <Details somevalue="value"/>
  </Secondary>
</Primary>

Обновлено с примером для Джека

Итак, в этом примере, Джек, я установил переменную num_secondarys, которая будет создавать X вторичных элементов, каждый из которых содержит элемент details, а значения в элементе detail будут увеличиваться на 1. Я создаю один элемент Secondary и detail какшаблон, а затем используйте Deepcopy для копирования их в качестве новых элементов.

from lxml import etree
from copy import deepcopy

top = etree.Element('Primary')
secondary = etree.Element('Secondary')
detail = etree.Element('Details', somevalue='value')
num_secondarys = 3
for i in range(1, num_secondarys + 1):
    this_secondary = deepcopy(secondary)
    this_detail = deepcopy(detail)
    this_detail.attrib['somevalue']+=str(i)
    this_secondary.append(this_detail)
    top.append(this_secondary)

print(etree.tostring(top, encoding="unicode", pretty_print=True))

OUTPUT

<Primary>
  <Secondary>
    <Details somevalue="value1"/>
  </Secondary>
  <Secondary>
    <Details somevalue="value2"/>
  </Secondary>
  <Secondary>
    <Details somevalue="value3"/>
  </Secondary>
</Primary>
0 голосов
/ 16 октября 2019

Ниже (с помощью ElementTree встроенная библиотека Python XML)

import xml.etree.ElementTree as ET

root = ET.Element('Primary')
for x in range(2):
  sec = ET.SubElement(root, 'Secondary')
  details = ET.Element('Details')
  details.attrib['somevalue'] = 'value'
  sec.append(details)
ET.dump(root)

output

<Primary>
   <Secondary>
      <Details somevalue="value" />
   </Secondary>
   <Secondary>
      <Details somevalue="value" />
   </Secondary>
</Primary>
...