【发布时间】: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>
我想选择所有具有相同类属性的相邻<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 的工具,或者是否像上面的通常方法那样手动实现?
【问题讨论】: