深くネストされた XML ツリーが与えられた場合、特定の要素を見つけたいと考えています。その時点で、上位の要素と同じレベルにある新しい要素で X をラップしたいと考えています。次に、「特定の」要素の後のポイントから元のツリーの残りの部分を続行したいと思います。
たとえば、次の入力があるとします。
<root>
<branch att="yo">
<div stuff="no">
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
</branch>
</root>
<ul> で項目 2 を検索したい (簡単に)。項目 2 の前に新しいブランチ レベルの要素を挿入したいと思います。次に、項目 2 を続行します (つまり、先祖ノードを継続します)。つまり、次の出力が必要です。
<root>
<branch att="yo">
<div stuff="no">
<ul>
<li>Item 1</li>
</ul>
</div>
</branch>
<branch>
<div>
<p>New branch here</p>
</div>
</branch>
<branch att="yo">
<div stuff="no">
<ul>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
</branch>
</root>
一般化されたソリューションを作成するために、これを開始するのは難しい問題です。ノード名と属性を見つけるための祖先ノードの処理とともに、複数のモードまたはキーのいずれかが必要になると思います。どんな助けでも大歓迎です。
さて、これは私がこれまでに持っているものです。部分的に機能します (たとえば、いくつかのノードをコピーしますが、属性はコピーしません。ノードもコピーしすぎますが、それは最初だと思います)。ここでの私の考えは、再帰関数が必要だということです。関心のある最も遠い祖先 (分岐) から開始し、最終的な子孫が特定の要素 (li "item 2") である子ノードごとに、各ノードとその属性をコピーして、前のノードに追加します (つまり、 newNewTree の目的)。そして、特定の要素 (li のアイテム 2) に到達するまで再帰します。その時点で、NewNewTree 変数を返します。これは、正しい祖先要素 (テキストなし) のツリーになることを目的としています。これを機能させるには、ノードとその属性をコピーする ID テンプレートのオーバーライドを使用して、関数から各ノードを処理する必要があると思います。
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:my="http://www.example.com"
exclude-result-prefixes="xs my"
version="2.0">
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:sequence select="$origNodesNoText" />
</xsl:template>
<xsl:variable name="ancestorElemName" select="'div'" />
<xsl:variable name="origNodesNoText">
<xsl:apply-templates select="/" mode="origNodesNoText"/>
</xsl:variable>
<xsl:template match="text()" mode="origNodesNoText"/>
<xsl:template match="li[.='Item 2']" mode="origNodesNoText">
<xsl:variable name="newTree">
<xsl:element name="{local-name(ancestor::*[local-name()=$ancestorElemName][1])}">
<xsl:for-each select="ancestor::*[local-name()=$ancestorElemName][1]/attribute::*">
<xsl:attribute name="{local-name()}" select="."/>
</xsl:for-each>
</xsl:element>
</xsl:variable>
<xsl:sequence select="my:split(ancestor::*[local-name()=$ancestorElemName][1],.,$newTree)" />
</xsl:template>
<xsl:function name="my:split">
<xsl:param name="ancestorElemNode" />
<xsl:param name="callingNode" />
<xsl:param name="newTree" />
<xsl:message>Calling my split</xsl:message>
<xsl:choose>
<xsl:when test="$ancestorElemNode ne $callingNode">
<xsl:message>Found a node to copy its name and attributes</xsl:message>
<xsl:variable name="newNewTree">
<xsl:for-each select="$newTree/node()">
<xsl:copy /> <!-- doesn't copy attribute nodes so not what we want -->
</xsl:for-each>
<xsl:element name="{local-name($ancestorElemNode)}">
<xsl:for-each select="$ancestorElemNode/attribute::*">
<xsl:attribute name="{local-name()}" select="."/>
</xsl:for-each>
</xsl:element>
</xsl:variable>
<xsl:sequence select="my:split($ancestorElemNode/child::*[descendant::*[. eq $callingNode]][1],$callingNode,$newNewTree)" />
</xsl:when>
<xsl:otherwise>
<xsl:message>Found the end point</xsl:message>
<xsl:sequence select="$newTree" />
</xsl:otherwise>
</xsl:choose>
</xsl:function>
</xsl:stylesheet>