1

XSLT を使用して、XML ドキュメントをある Doctype から別の Doctype に変換しています。簡略化した形式では、次のようなものがあります。

<section>
    <title>Title</title>
    <a>content1</a>
    <a>content2</a>
    <a>content3</a>
    <a>content4</a>
    <b>other content</b>
    <a>content5</a>
    <a>content6</a>
    <a>content7</a>
    <c>other content</c>
    <a>content8</a>
    <a>content9</a>
    <a>content10</a>
</section>

私が望むのは、<a>要素のグループの周りにラッパーを配置することです。そのため、翻訳された結果は次のようになります。

<section>
    <title>Title</title>
    <a-wrapper>
        <a>content1</a>
        <a>content2</a>
        <a>content3</a>
        <a>content4</a>
    </a-wrapper>
    <b>other content</b>
    <a-wrapper>
        <a>content5</a>
        <a>content6</a>
        <a>content7</a>
    </a-wrapper>
    <c>other content</c>
    <a-wrapper>
        <a>content8</a>
        <a>content9</a>
        <a>content10</a>
    </a-wrapper>
</section>

私の現在のテンプレートは次のようになります。

<xsl:template match="a">
    <xsl:choose>
        <xsl:when test="not(preceding-sibling::a)">
            <xsl:element name="a-wrapper">
                <xsl:element name="a">
                    <xsl:apply-templates/>
                </xsl:element>
                <xsl:apply-templates select="following-sibling::a[generate-id(preceding-sibling::a[last()]) = generate-id(current())]"/>
            </xsl:element>
        </xsl:when>
        <xsl:otherwise>
            <xsl:element name="a">
                <xsl:apply-templates/>
            </xsl:element>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

これは私をそこに途中で連れて行きます。問題は、apply-templates タグの select 句がすべてのタグを取得している<a>ことです。最初の非タグで停止したい場合<a>、つまり、最初のタグの処理によって<a>10 個すべてのタグが最初のタグ内に配置さ<a>れます。<a-wrapper>最初の 4 つだけが必要な場合。<a-wrapper>これには、5 番目のタグによって生成されるようなネストされたタグを挿入する効果もありますが、これ<a>は DTD では許可されていません。

問題は、最初の非<a>タグで終わる次の兄弟のサブセットをどのように選択するかです。

4

2 に答える 2

3

あなたの例から、セクション要素内の最初の要素を除くすべてを「ラップ」したいと仮定すると、セクション要素に一致するテンプレートを記述してから、最も前の兄弟とは異なる名前の子要素を選択することから始めます

<xsl:apply-templates 
     select="*[position() > 1]
              [local-name() != local-name(preceding-sibling::*[1])]" mode="wrapper" />

次に、この「ラッパー」テンプレート内でラッパー要素を作成し、ラップする最初の要素を選択することから始めます。

  <xsl:element name="{local-name()}-wrapper">
     <xsl:apply-templates select="self::*" mode="copy"/>
  </xsl:element>

そして、この「コピー」テンプレート内で、要素をコピーしてから、同じ名前の次の要素を再帰的に選択します。

<xsl:apply-templates 
     select="following-sibling::*[1][local-name() = local-name(current())]" mode="copy"/>

ここに完全な XSLT があります

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml" indent="yes"/>

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

   <xsl:template match="/*">
      <xsl:copy>
         <xsl:apply-templates select="*[1]"/>
         <xsl:apply-templates select="*[position() &gt; 1][local-name() != local-name(preceding-sibling::*[1])]" mode="wrapper"/>
     </xsl:copy>
   </xsl:template>

   <xsl:template match="*" mode="wrapper">
      <xsl:element name="{local-name()}-wrapper">
         <xsl:apply-templates select="self::*" mode="copy"/>
      </xsl:element>
   </xsl:template>

   <xsl:template match="*" mode="copy">
      <xsl:call-template name="identity"/>
      <xsl:apply-templates select="following-sibling::*[1][local-name() = local-name(current())]" mode="copy"/>
   </xsl:template>
</xsl:stylesheet>

XML に適用すると、以下が出力されます。

<section>
   <title>Title</title>
   <a-wrapper>
      <a>content1</a>
      <a>content2</a>
      <a>content3</a>
      <a>content4</a>
   </a-wrapper>
   <b-wrapper>
      <b>other content</b>
   </b-wrapper>
   <a-wrapper>
      <a>content5</a>
      <a>content6</a>
      <a>content7</a>
   </a-wrapper>
   <c-wrapper>
      <c>other content</c>
   </c-wrapper>
   <a-wrapper>
      <a>content8</a>
      <a>content9</a>
      <a>content10</a>
   </a-wrapper>
</section>

このソリューションでは、特定の要素名がハードコーディングされていないことに注意してください。これにより、柔軟性が向上することが期待されます。

編集:実際には「a」要素のみをラップしたいので、これにより実際には簡単になります。次のように、「a」要素のグループの最初の出現に一致するテンプレートを作成できます

<xsl:template match="a[preceding-sibling::*[1][not(self::a)]]">

次に、要素をコピーし、次の「a」要素を前と同様の方法でコピーします。

代わりにこの XSLT を試してください

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml" indent="yes"/>

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

   <xsl:template match="a[preceding-sibling::*[1][not(self::a)]]">
        <a-wrapper>
            <xsl:apply-templates select="self::a" mode="copy"/>
        </a-wrapper>
   </xsl:template>

   <xsl:template match="a" />

   <xsl:template match="a" mode="copy">
      <xsl:call-template name="identity"/>
      <xsl:apply-templates select="following-sibling::*[1][self::a]" mode="copy"/>
   </xsl:template>
</xsl:stylesheet>
于 2013-05-03T22:39:46.210 に答える
0

私に代わってあなたの努力に心から感謝します。問題の説明が不完全だったので、今度は私が謝る番です。[a] を別のタグに変換していることを忘れていました - [a2] としましょう。[section] から [sect1] への変換など、他の変換も行われています。

それまでの間、別の情報源が私にとって有効な解決策を提案してくれました。フラグメント形式では、次のようになります。

<xsl:template match="section">
    <xsl:element name="sect1">
        <xsl:for-each-group select="*" group-adjacent="boolean(self::a)">
            <xsl:choose>
                <xsl:when test="current-grouping-key()">
                    <xsl:element name="a-wrapper">
                        <xsl:apply-templates select="current-group()"/>
                    </xsl:element>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:apply-templates select="current-group()"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:for-each-group>
    </xsl:element>
</xsl:template>
于 2013-05-07T19:54:06.207 に答える