【问题标题】:Rewrite XML to wrap multiple select nodes into a new one重写 XML 以将多个选择节点包装成一个新节点
【发布时间】:2013-10-27 09:17:36
【问题描述】:

我正在寻找更好的解决方案来执行以下操作。给定一个 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>

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

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

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.

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

获得该列表后,我创建一个新的&lt;wrap&gt; 元素,复制class 属性,然后将所有同级元素添加到新元素中。完成后,我添加了新元素(将所有 &lt;p&gt; 兄弟姐妹作为其孩子),原来的第一个兄弟姐妹曾经是:

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 的工具,或者是否像上面的通常方法那样手动实现?

【问题讨论】:

标签: python xml xslt xpath


【解决方案1】:

您使用的是 (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>

【讨论】:

  • 我正在使用 lxml,感谢您的提醒!好奇是否有另一种方式来描述转换,而不是手动实现它。
  • 不能简化太多,上面确实避免了原始解决方案在迭代时避免修改列表的一些尴尬......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-27
  • 1970-01-01
  • 2011-04-08
相关资源
最近更新 更多