我正在寻找更好的解决方案来执行以下操作。给定一个 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 的工具,或者是否像上面的通常方法那样手动实现?