-1

我正在寻找更好的解决方案来执行以下操作。给定一个 XML 树,例如

<root>
  <x>
    <p class="a"> </p>
    <p class="b"> </p>  <!-- Not yet wrapped, so these two <p> nodes can be wrapped, like below. -->
    <p class="b"> </p>
    <p class="a"> </p>  <!-- Not yet wrapped, so these two <p> nodes can be wrapped. -->
    <p class="a"> </p>
  </x>
  <y>
    <p class="a"> </p>
    <wrap class="b">    <!-- The <p> nodes are wrapped in a <wrap> and the attribute has moved. -->
      <p> </p>
      <p> </p>
    </wrap>
    <p class="a"> </p>
  </y>
</root>

我想选择所有<p>具有相同类属性的相邻节点,并将它们包装在<wrap>同一类的元素中;但是,如果它们已经包裹起来,请不要包裹它们。

我目前的方法是选择所有候选包装节点:

candidates = xml.xpath("//*[not(self::wrap)]/p[@class]")

然后选择任何候选人并将其所有有效的兄弟姐妹累积到一个列表中:

if len(candidates) == 0 :
    return
candidate = candidates[0] # Pick any one of the candidates.
siblings = get_siblings(candidate) # Gather all of the candidate's matching sibling elements into a list.

构建同级列表很简单:给定一个候选<p>元素,遍历所有getprevious()匹配的元素(即也是<p class="b">元素),直到找到第一个同级。然后将所有元素收集getnext()到一个列表中,从而产生一组完整的匹配元素。

获得该列表后,我创建一个新<wrap>元素,复制class属性,然后将所有同级元素添加到新元素中。完成后,我<p>在原来的第一个兄弟元素所在的位置添加了新元素(将所有兄弟姐妹作为其子元素):

parent = candidate.getparent() # Get parent element of the candidate.
index = parent.index(candidate) # Get the index of the candidate.

wrap = lxml.etree.Element("wrap") # Create a new <wrap> element...
wrap.attrib["class"] = candidate.attrib["class"] # ...and copy the class attribute.
for s in siblings : # Iterate over all sibling elements in the list, and
    wrap.append(s) # add the sibling to the new <wrap> element, and
    del s.attrib["class"] # remove the class attribute from the sibling (because it's in the <wrap> element.
parent.insert(index, wrap) # Once all siblings are moved, add the new <wrap> element where the siblings used to be.

问题

环顾四周,似乎有比手动实现这种重写更好的解决方案,例如使用XSLT?(我以前从未使用过它,所以我不确定 XSLT 是否旨在解决此类任务。)那么:解决此问题的“正确”方法是什么?是否有用于此类重写/转换的更正式的基于 XML 的工具,或者是否像上面的通常方法那样手动实现?

4

1 回答 1

0

您使用的是 (c)ElementTree 还是 lxml?他们对以下有不同的行为。当将一个节点从一个父节点“移动”到另一个节点时,ElementTree 创建一个副本,而 lxml 重新定义该节点。这种行为可能对你有用......

import xml.etree.ElementTree as et
from lxml import etree as lxet

e1 = et.Element('e1')
e2 = et.Element('e2')
e3 = et.SubElement(e1, 'e3')
e2.append(e3)

et.dump(e1)
<e1><e3 /></e1>
et.dump(e2)
<e2><e3 /></e2>

lxe1 = lxet.Element('e1')
lxe2 = lxet.Element('e2')
lxe3 = lxet.SubElement(lxe1, 'e3')
lxe2.append(lxe3)

lxet.dump(lxe1)
<e1/>
lxet.dump(lxe2)
<e2>
  <e3/>
</e2>
于 2013-10-18T13:50:35.867 回答