2

深くネストされた 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>
4

2 に答える 2

0

別のオプションがあります。基本的にあなた:

  • ターゲット要素 (パラメーター) の値を定義しますtarget
  • ターゲット要素 (キーbranch) を特定し、その要素のテンプレートを適用します。(その値を持つ要素が複数ある場合は、最初の要素で分岐します。)
  • 上から始まる先行兄弟のテンプレートを適用します。注:「トップ」は/*/*です。ルート要素に複数の子がある場合、このスタイルシートを調整する必要があります。
  • テンプレートをターゲット要素に適用します。param ( branch) を渡すので、ここに新しいブランチが挿入されることがわかります。
  • branch次の兄弟とともに、テンプレートをターゲット要素に (パラメーターなしで) 再度適用します。

XML 入力

<root>
    <branch att="yo">
        <div stuff="no">
            <ul>
                <li>Item 1</li>
                <li>Item 2</li>
                <li>Item 3</li>
            </ul>
        </div>        
    </branch>
</root>

XSLT 2.0

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:param name="target" select="'Item 2'"/>
    <xsl:key name="branch" match="*[.=$target]" use="."/>

    <xsl:template match="/*">
        <xsl:copy>
            <xsl:apply-templates select="@*|(//*[key('branch',.)])[1]"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="*">
        <xsl:variable name="precID" select="for $id in preceding-sibling::* return generate-id($id)"/>
        <xsl:variable name="follID" select="for $id in (self::*,following-sibling::*) return generate-id($id)"/>
        <xsl:apply-templates select="/*/*" mode="ident">
            <xsl:with-param name="restricted2IDs" select="$precID" tunnel="yes"/>           
        </xsl:apply-templates>
        <xsl:apply-templates select="/*/*" mode="ident">
            <xsl:with-param name="restricted2IDs" select="generate-id(current())" tunnel="yes"/>
            <xsl:with-param name="branch" select="';-)'" tunnel="yes"/>
        </xsl:apply-templates>
        <xsl:apply-templates select="/*/*" mode="ident">
            <xsl:with-param name="restricted2IDs" select="$follID" tunnel="yes"/>           
        </xsl:apply-templates>      
    </xsl:template>

    <xsl:template match="@*|node()" mode="ident">
        <xsl:param name="restricted2IDs" tunnel="yes"/>
        <xsl:param name="branch" tunnel="yes"/>
            <xsl:choose>
                <xsl:when test="*[generate-id()=$restricted2IDs]">
                    <xsl:choose>
                        <xsl:when test="$branch">
                            <p>New branch here</p>
                        </xsl:when>
                        <xsl:otherwise>
                            <xsl:copy>
                                <xsl:apply-templates select="@*|node()[generate-id()=$restricted2IDs]" mode="ident"/>                                                       
                            </xsl:copy>                         
                        </xsl:otherwise>
                    </xsl:choose>
                </xsl:when>
                <xsl:when test="descendant::*[generate-id()=$restricted2IDs]">
                    <xsl:copy>
                        <!--If you want the attributes in the new branch, remove the "if" and just use "@*|node".-->
                        <xsl:apply-templates select="if ($branch) then node() else @*|node()" mode="ident"/>                        
                    </xsl:copy>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:copy>
                        <xsl:apply-templates select="@*|node()" mode="ident"/>
                    </xsl:copy>
                </xsl:otherwise>
            </xsl:choose>
    </xsl:template>

</xsl:stylesheet>

XML 出力

<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>
于 2013-08-31T02:34:02.450 に答える
0

次の方法で入力のノードを処理したいとします。最初に、頻繁に参照する必要があるノードの 2 つの用語を定義しましょう。

  • 2 番目のリスト項目はターゲット要素です。これをブルズアイと呼びます。
  • ブルズアイを囲む分岐要素を分割する必要があります。バナナと呼びます。

これで、出力ツリーへの対応に関して、入力ツリーでノードのいくつかのクラスを定義できます。

  • Banana の外側にあるものはすべて恒等変換のようにコピーされます。出力内の同じタイプの同じ名前のノードに対応し、1 つの例外を除いて、属性と子のシーケンスが似ています。例外は、Banana の親です。その一連の子には、Banana に対応する 2 つの要素と、branchそれらの間にある 3 番目の要素 (ここでは という名前) が含まれます。
  • Bullseye の祖先であり、Banana の子孫 (または Banana 自体) であるすべての要素は、出力ツリー内の類似した名前と属性を持つ 2 つの要素に対応します。
  • ドキュメント順序でブルズアイより前にあり、ブルズアイの祖先ではなく、ブルズアイの祖先の属性ではないバナナのすべての子孫は、バナナの最初のコピー内で恒等変換のように出力に 1 回コピーされます。 .
  • ドキュメント順でブルズアイに続き、ブルズアイの祖先ではなく、ブルズアイの祖先の属性でもないバナナのすべての子孫は、バナナの 2 番目のコピー内で恒等変換のように出力に 1 回コピーされます。 . ブルズアイ自体もそうです。

私はこれをほぼ恒等変換で行います。バナナとその子孫には特別な扱いが必要です。それらの一部は 2 回コピーする必要があり、一部は 1 回だけコピーする必要がありますが、最も簡単なことは、サブツリー全体を 2 回処理することです。

スタイルシート全体は次のようになります。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="2.0">

  <xsl:output method="xml" indent="yes"/>

  <xsl:variable name="Bullseye" select="//li[.='Item 2']"/>

  <!--* Default identity transform *-->
  <xsl:template match="comment()">
    <xsl:comment><xsl:value-of select="."/></xsl:comment>
  </xsl:template>

  <xsl:template match="processing-instruction()">
    <xsl:variable name="pitarget" select="name()"/>
    <xsl:processing-instruction name="{$pitarget}">
      <xsl:value-of select="."/>
    </xsl:processing-instruction>
  </xsl:template>

  <xsl:template match="@*|*">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <!--* Banana *-->
  <xsl:template match="branch[descendant::* intersect $Bullseye]">    
    <xsl:copy>
      <xsl:apply-templates select="@*|node()" mode="pre-bullseye"/>
    </xsl:copy>
    <xsl:call-template name="insertion"/>
    <xsl:copy>
      <xsl:apply-templates select="@*|node()" mode="post-bullseye"/>
    </xsl:copy>
  </xsl:template>

  <!--* first pass over subtree rooted at Banana *-->
  <xsl:template match="comment()" mode="pre-bullseye">
    <xsl:apply-templates select="."/>
  </xsl:template>
  <xsl:template match="processing-instruction()" mode="pre-bullseye">
    <xsl:apply-templates select="."/>
  </xsl:template>
  <xsl:template match="@*|*|text()" mode="pre-bullseye">
    <xsl:choose>
      <xsl:when test="descendant::* intersect $Bullseye">
        <xsl:copy>
          <xsl:apply-templates select="@*"/>
          <xsl:apply-templates select="node()" mode="pre-bullseye"/>
        </xsl:copy>
      </xsl:when>
      <xsl:when test=". &lt;&lt; $Bullseye">
        <xsl:copy>
          <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
      </xsl:when>
      <xsl:when test=". is $Bullseye"/>      
      <xsl:when test=". >> $Bullseye"/>
      <xsl:otherwise>
        <xsl:message terminate="yes"
          >Unexpected case in mode pre-bullseye, dying now.</xsl:message>
      </xsl:otherwise>
    </xsl:choose>

  </xsl:template>

  <!--* Second pass over subtree rooted at Banana *-->
  <xsl:template match="comment()" mode="post-bullseye">
    <xsl:apply-templates select="."/>
  </xsl:template>
  <xsl:template match="processing-instruction()" mode="post-bullseye">
    <xsl:apply-templates select="."/>
  </xsl:template>
  <xsl:template match="@*|*|text()" mode="post-bullseye">
    <xsl:choose>
      <xsl:when test="descendant::* intersect $Bullseye">
        <xsl:copy>
          <xsl:apply-templates select="@*"/>
          <xsl:apply-templates select="node()" mode="post-bullseye"/>
        </xsl:copy>
      </xsl:when>
      <xsl:when test=". &lt;&lt; $Bullseye"/>

      <xsl:when test=". is $Bullseye">
        <xsl:copy-of select="."/>
      </xsl:when>
      <xsl:when test=". >> $Bullseye">
        <xsl:copy>
          <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
      </xsl:when>
      <xsl:otherwise>
        <xsl:message terminate="yes"
         >Unexpected case in post-bullseye, dying now.</xsl:message>
      </xsl:otherwise>
    </xsl:choose>

  </xsl:template>

  <xsl:template name="insertion">
    <branch>
      <div>
        <p>New branch here.</p>
      </div>
    </branch>
  </xsl:template>

</xsl:stylesheet>

指定した入力に対して実行すると、このスタイルシートは次の出力を生成します。

<?xml version="1.0" encoding="UTF-8"?>
<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>
      <div stuff="no">
         <ul>
            <li>Item 2</li>
                <li>Item 3</li>
            </ul>
        </div>        
    </branch>
</root>
于 2013-08-31T00:36:52.417 に答える