1

この質問と同様に (関連するエントリは他にもありますが、新しいユーザーとして投稿できる URL は 1 つだけです): Xpath Get elements that are between 2 elements

「その他/区切り」要素間で発生する一連の要素の選択に関して質問があります。この状況は、XSLT を使用してフラットな HTML テーブルを階層的な XML 構造に変換しようとしたときに発生します。テンプレートで再帰を使用しようとしましたが、おそらく私のせいでデッドロックが発生したため、saxon はこれを受け入れることを拒否しましたが、最初から始めましょう。

最初のソース データは HTML テーブルです。

<table >
    <thead>
        <tr>
            <th>Column 1</th>
            <th>Column 2</th>
            <th>Column 3</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <th colspan="3" >Group 1</th>
        </tr>
        <tr>
            <td>attribute 1.1.1</td>
            <td>attribute 1.1.3</td>
            <td>attribute 1.1.2</td>
        </tr>
        <tr>
            <td>attribute 1.2.1</td>
            <td>attribute 1.2.2</td>
            <td>attribute 1.2.3</td>
        </tr>
        <tr>
            <td>attribute 1.3.1</td>
            <td>attribute 1.3.2</td>
            <td>attribute 1.3.3</td>
        </tr>
        <tr>
            <th colspan="3" >Group 2</th>
        </tr>
        <tr>
            <td>attribute 2.1.1</td>
            <td>attribute 2.1.3</td>
            <td>attribute 2.1.2</td>
        </tr>
        <tr>
            <td>attribute 2.2.1</td>
            <td>attribute 2.2.2</td>
            <td>attribute 2.2.3</td>
        </tr>
        <tr>
            <td>attribute 2.3.1</td>
            <td>attribute 2.3.2</td>
            <td>attribute 2.3.3</td>
        </tr>
    </tbody>
</table>

XML でのターゲット出力は次のようになります。

 <groups>
    <group name="Group 1">
        <item attribute1="attribute 1.1.1" attribute2="attribute 1.1.3" attribute3="attribute 1.1.2"/>
        <item attribute1="attribute 1.2.1" attribute2="attribute 1.2.2" attribute3="attribute 1.2.3"/>
        <item attribute1="attribute 1.3.1" attribute2="attribute 1.3.2" attribute3="attribute 1.3.3"/>
    </group>
    <group name="Group 2">
        <item attribute1="attribute 2.1.1" attribute2="attribute 2.1.3" attribute3="attribute 2.1.2"/>
        <item attribute1="attribute 2.2.1" attribute2="attribute 2.2.2" attribute3="attribute 2.2.3"/>
        <item attribute1="attribute 2.3.1" attribute2="attribute 2.3.2" attribute3="attribute 2.3.3"/>
    </group>
</groups>

したがって、すべての項目エントリ (TR 要素) を取得し、それらをグループに追加したいと考えています。これは基本的に、子として TH 要素を持つ要素に遭遇するまで、後続の兄弟 TR 要素をすべて選択することになります。TH 子を持つこの最初の TR の位置のみを特定でき、グループの新しい見出しを示すことができる場合、これは次のように行うことができます。

<xsl:for-each select="tbody/tr">
    <xsl:if test="th">
        <xsl:element name="group">
            <xsl:attribute name="name"><xsl:value-of select="th"/></xsl:attribute>
            <xsl:for-each select="following-sibling::tr[position() < $positionOfNextThElement]">            
                <xsl:call-template name="item"/>
            </xsl:for-each>
        </xsl:element>
    </xsl:if>
</xsl:for-each>

ただし、最初に遭遇した TR/TH タグの位置を特定できません。

前述のように、テンプレートで再帰を使用してみました。常に「アイテム」テンプレートを呼び出し、このテンプレートで次のアイテムでもそれを呼び出すかどうかを決定します。問題は、テンプレート内からのテンプレートの呼び出しにあると思います。コンテクスト内のアイテムが増えない?どの項目に取り組んでいるかを判断するためにパラメーターを渡す必要がありますか?

とにかく、これは私が思いついたものでした:

<xsl:for-each select="tbody/tr">
    <xsl:if test="th">
        <xsl:element name="group">
            <xsl:attribute name="name"><xsl:value-of select="th"/></xsl:attribute>
            <xsl:call-template name="item"/>
        </xsl:element>
    </xsl:if>
</xsl:for-each>

<xsl:template name="item">
    <xsl:element name="item">
        <xsl:attribute name="attribute1"><xsl:value-of select="following-sibling::tr[1]/td[1]"/></xsl:attribute>
        <xsl:attribute name="attribute2"><xsl:value-of select="following-sibling::tr[1]/td[2]"/></xsl:attribute>
        <xsl:attribute name="attribute2"><xsl:value-of select="following-sibling::tr[1]/td[3]"/></xsl:attribute>
    </xsl:element>
    <!-- When the next element has not got a TH tag, continue with invoking this template -->
    <xsl:if test="count(following-sibling::tr[1]/th) != 1">
        <xsl:call-template name="item"/>
    </xsl:if>
</xsl:template>

これを実現する方法についての提案は大歓迎です!

4

2 に答える 2

1

テンプレート「item」を再帰的に呼び出してもコンテキストが増加しない理由は、xs:call-template が常に現在のコンテキスト アイテムをコンテキストとして渡すためです。ご覧のとおり、変換は無限再帰に入ります。

常に正確に 3 つの属性を生成する必要があると仮定すると、再帰は必要ありません。

これを試して:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

    <xsl:template match="table">
        <groups>
            <xsl:apply-templates select="tbody/tr[th]"/>
        </groups>
    </xsl:template>

    <xsl:template match="tr[th]">
        <xsl:variable name="id" select="generate-id(.)"/>
        <group name="{string(th)}">
            <xsl:apply-templates
                select="following-sibling::tr[not(th)][generate-id(preceding-sibling::tr[th][1]) = $id]"/>
        </group>
    </xsl:template>

    <xsl:template match="tr">
        <item attribute1="{td[1]}" attribute2="{td[2]}" attribute3="{td[3]}" />                    
    </xsl:template>

</xsl:stylesheet>

これは、各ヘッダー行にテンプレートを適用することで機能します。これらのテンプレートのそれぞれは、その複雑な xpath を使用して、「その」後続の行を呼び出します。これは、その特定の行がヘッダーを持つ最初の先行行としてある後続の兄弟行です。

もちろん、属性の数が異なる場合は、そこを再帰して、位置を示すパラメーターを渡す必要があります。

XSLT グループ化には確立された方法がいくつかありますが、そのうちの 1 つは、あなたが行っていたように再帰的です。もう 1 つの方法は Muenchian グループ化と呼ばれます。良い記事はここにあります。

于 2009-10-10T14:43:08.563 に答える
0

再帰なしの可変属性カウントに適合する代替ソリューション。

<xsl:stylesheet 
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>

  <xsl:template match="table">
    <groups>
      <xsl:apply-templates select="tbody/tr[th]"/>
    </groups>
  </xsl:template>

  <xsl:template match="tr[th]">
    <group name="{th}">
      <xsl:apply-templates select="
        following-sibling::tr[not(th)][
          generate-id(preceding-sibling::tr[th][1]) = generate-id(current())
        ]
      "/>
    </group>
  </xsl:template>

  <xsl:template match="tr">
    <item>
     <xsl:apply-templates select="td" />
    </item>
  </xsl:template>

  <xsl:template match="td">
    <xsl:attribute name="attribute{position()}">
      <xsl:value-of select="." />
    </xsl:attribute>
  </xsl:template>

</xsl:stylesheet>
于 2009-10-11T10:14:30.583 に答える