次のことを行うためのより良い解決策を探しています。与えられた 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 を使用したことがないので、XSLT がそのようなタスクを解決することを意図しているかどうかはわかりません。) では、これを行う「適切な」方法は何ですか? そのような書き換え/変換のためのより正式な XML ベースのツールはありますか、それとも上記のような手動での実装は通常の方法ですか?