3

XSLT を使用して、列に特定の値 (この場合は「I Want You」というテキスト) が含まれている場合、特定の列内のすべてのノードに値 (この場合は「I added This」という文字列) を追加しようとしています。 2.0。

これは、次の表がある場合を意味します。

<table>
        <tr>
            <th>header value</th>
            <th>I Want You</th>
            <th>header value</th>
        </tr>

        <tr>
            <td>value 1</td>
            <td>value 2</td>
            <td>value 3</td>
        </tr>

        <tr>
            <td>value 4</td>
            <td>value 5</td>
            <td>value 6</td>
        </tr>
</table>

xslt スクリプトを適用した後の出力は次のようになります。

<table>
        <tr>
            <th>header value</th>
            <th>I Want You</th>
            <th>header value</th>
        </tr>

        <tr>
            <td>value 1</td>
            <td>I Added This value 2</td>
            <td>value 3</td>
        </tr>

        <tr>
            <td>value 4</td>
            <td>I Added This value 5</td>
            <td>value 6</td>
        </tr>
</table>

それを達成するために、私は多くの異なるスタイルシートを書きましたが、近づくことさえできませんでした:

テーブル全体のコピー:

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

これにより、変更したい列の値が取得されますが、そこから、「この値を追加しました」というフレーズを各 td 値の先頭に追加する方法がわかりません。

<xsl:template match="//th[contains(lower-case(.), 'I Want You')]">

    <xsl:variable name="tdPos" select="count(preceding-sibling::td)+2"/>

    <xsl:for-each select="current()/parent::*/following-sibling::tr">
        <xsl:value-of select="./td[$tdPos]/text()"/>
    </xsl:for-each>
</xsl:template>

私を正しい方向に向けるかもしれないヒントは大歓迎です!

4

6 に答える 6

1

おそらく最短で最も単純な変換(no preceding-sibling::、no position()、条件付き命令、変数なし)

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

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

 <xsl:template match="tr/*[2]/text()[not(. = 'I Want You')]">
  <xsl:value-of select="concat('I Added This ', .)"/>
 </xsl:template>
</xsl:stylesheet>

この変換が提供されたXMLドキュメントに適用される場合:

<table>
    <tr>
        <th>header value</th>
        <th>I Want You</th>
        <th>header value</th>
    </tr>
    <tr>
        <td>value 1</td>
        <td>value 2</td>
        <td>value 3</td>
    </tr>
    <tr>
        <td>value 4</td>
        <td>value 5</td>
        <td>value 6</td>
    </tr>
</table>

必要な正しい結果が生成されます。

<table>
   <tr>
      <th>header value</th>
      <th>I Want You</th>
      <th>header value</th>
   </tr>
   <tr>
      <td>value 1</td>
      <td>I Added This value 2</td>
      <td>value 3</td>
   </tr>
   <tr>
      <td>value 4</td>
      <td>I Added This value 5</td>
      <td>value 6</td>
   </tr>
</table>

説明

  1. IDルールは、一致したすべてのノードを「現状のまま」コピーします。

  2. オーバーライドするテンプレートが1つあります。これは、文字列値が文字列ではなく、'I Want You' 2番目の要素の子である任意の要素の子である(したがって、両方thと一致する)テキストノードと一致します。このテンプレートは、文字列の連結と現在の(一致した)ノードの文字列値を出力します。tdtr'I Added This '

:これはXSLT 1.0ソリューションですが、XSLT 2.0プロセッサでも同じように機能し、変更はまったくありません。versionの属性xsl:stylesheetを「2.0」に変更しても安全です(必要な場合) 。

于 2012-06-21T02:47:01.253 に答える
1

注目に値する 1 つの問題は、「含む」テストが正しくないことです。

<xsl:template match="//th[contains(lower-case(.), 'I Want You')]"> 

それはおそらく次の

<xsl:template match="//th[contains(lower-case(.), lower-case('I Want You'))]"> 

ただし、テンプレート マッチでそのようなパラメーターを使用することはできませんが、検索値をパラメーター化した方が全体的に優れている場合があります。代わりに、任意のセルに一致するだけです....

<xsl:template match="tr/*">

次に、前の行の同等の列に必要な値があるかどうかをテストできます

<xsl:if 
   test="../preceding-sibling::tr/*[$position][contains(., $match)]">

(簡潔にするために、lower-case() コマンドは削除されました)

ここで、$position は現在のセル位置を含む変数で、$match は探している変数です。

次の XSLT を試してください

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="html" indent="yes"/>
   <xsl:param name="match" select="'I Want You'" />
   <xsl:param name="add" select="'I Added This'" />

   <xsl:template match="tr/*">
      <xsl:variable name="position" select="count(preceding-sibling::*) + 1"/>          <xsl:copy>
         <xsl:apply-templates select="@*"/>
         <xsl:if test="../preceding-sibling::tr/*[$position][contains(lower-case(.), lower-case($match))]">
            <xsl:value-of select="concat($add, ' ')" />
         </xsl:if>
         <xsl:apply-templates select="text()"/>
      </xsl:copy>
   </xsl:template>

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

XML に適用すると、次のように生成されます。

<table>
<tr>
   <th>header value</th>
   <th>I Want You</th>
   <th>header value</th>
</tr>
<tr>
   <td>value 1</td>
   <td>I Added This value 2</td>
   <td>value 3</td>
</tr>
<tr>
   <td>value 4</td>
   <td>I Added This value 5</td>
   <td>value 6</td>
</tr>
</table>

そして、この XML に適用すると

<table>
   <tr>
      <th>header value</th>
      <th>header value</th>
      <th>header value</th>
   </tr>
   <tr>
      <td>value 1</td>
      <td>I Want You</td>
      <td>value 2</td>
   </tr>
   <tr>
      <td>value 3</td>
      <td>value 4</td>
      <td>value 5</td>
   </tr>
</table>

以下が出力されます

<table>
<tr>
   <th>header value</th>
   <th>header value</th>
   <th>header value</th>
</tr>
<tr>
   <td>value 1</td>
   <td>I Want You</td>
   <td>value 2</td>
</tr>
<tr>
   <td>value 3</td>
   <td>I Added This value 4</td>
   <td>value 5</td>
</tr>
</table>
于 2012-06-20T14:26:41.877 に答える
1

あなたは正しい行にいました.ノードを出力するときに条件を実行する必要があっただけです.TDノードの場合、同じインデックスのいとこのTHノードが必要なテキストを備えているかどうかを確認します.

<xsl:template match="*|@*">
    <xsl:variable name='curr_pos' select='position()' />
    <xsl:copy>
        <xsl:apply-templates select='@*' />
        <xsl:if test='name() = "td" and /table/tr[1]/th[$curr_pos] = "I Want You"'>prefix - </xsl:if>
        <xsl:value-of select='text()' />
        <xsl:apply-templates select='*' />
    </xsl:copy>
</xsl:template>

このプレイグラウンド セッションで実行できます。

于 2012-06-20T14:42:13.490 に答える
0

ありがとうティムC!それは魅力のように機能しており、その上で本当に素晴らしいソリューションです! ドキュメントに複数のテーブルがある場合は少し問題がありますが、このスタイルシートを適用します。また、「I Want You」の値を含まないテーブルにも値が追加されます。そのための回避策はありますか?すべてのテーブルを選択してから、その列を個別に実行しようとしましたが、結果は出力されません。

<xsl:template match="//table">
  <xsl:for-each select="/tr/*">
    <xsl:variable name="position" select="position()" />
    <xsl:copy>
        <xsl:apply-templates select="@*"/>
        <xsl:if test="../preceding-sibling::tr/*[position() = $position][contains(lower-case(.), lower-case($match))]">
            <xsl:value-of select="concat($add, ' ')" />
        </xsl:if>
        <xsl:apply-templates select="text()"/>
    </xsl:copy>
  </xsl:for-each>
</xsl:template>
于 2012-06-20T15:09:18.060 に答える
0

これは、「I Want This」を任意で処理し、<th>そのようなテーブルが多数あるドキュメントで機能する短い正しい答えです(意味的に正しいはずです)。

この XSLT の場合:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output omit-xml-declaration="no" indent="yes"/>
  <xsl:strip-space elements="*"/>

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

  <!-- Template #2 -->
  <xsl:template match="tr[position() &gt; 1]/td[position() = count(../../th[. = 'I Want You']/preceding-sibling::th) + 1]/text()">
    <xsl:value-of select="concat('I Added This ', .)" />
  </xsl:template>

</xsl:stylesheet>

は、次の XML ドキュメントに適用されます。

<table>
  <tr>
    <th>header value</th>
    <th>I Want You</th>
    <th>header value</th>
  </tr>
  <tr>
    <td>value 1</td>
    <td>value 2</td>
    <td>value 3</td>
  </tr>
  <tr>
    <td>value 4</td>
    <td>value 5</td>
    <td>value 6</td>
  </tr>
</table>

必要な結果が生成されます。

<?xml version="1.0" encoding="UTF-8"?>
<table>
  <tr>
    <th>header value</th>
    <th>I Want You</th>
    <th>header value</th>
  </tr>
  <tr>
    <td>value 1</td>
    <td>I Added This value 2</td>
    <td>value 3</td>
  </tr>
  <tr>
    <td>value 4</td>
    <td>I Added This value 5</td>
    <td>value 6</td>
  </tr>
</table>

ドキュメントにそのような要素が複数<table>ある場合、この XSLT はそのケースにも対応することに注意してください。これはあなたが具体的に求めたものではありませんが、可能性の範囲外ではないので、カバーすることにしました。

XML:

<body>
  <table>
    <tr>
      <th>header value</th>
      <th>I Want You</th>
      <th>header value</th>
    </tr>
    <tr>
      <td>value 1</td>
      <td>value 2</td>
      <td>value 3</td>
    </tr>
    <tr>
      <td>value 4</td>
      <td>value 5</td>
      <td>value 6</td>
    </tr>
  </table>
  <table>
    <tr>
      <th>I Want You</th>
      <th>header value</th>
      <th>header value</th>
    </tr>
    <tr>
      <td>value 1</td>
      <td>value 2</td>
      <td>value 3</td>
    </tr>
    <tr>
      <td>value 4</td>
      <td>value 5</td>
      <td>value 6</td>
    </tr>
  </table>
</body>

結果:

<?xml version="1.0" encoding="UTF-8"?><body>
  <table>
    <tr>
      <th>header value</th>
      <th>I Want You</th>
      <th>header value</th>
    </tr>
    <tr>
      <td>value 1</td>
      <td>I Added This value 2</td>
      <td>value 3</td>
    </tr>
    <tr>
      <td>value 4</td>
      <td>I Added This value 5</td>
      <td>value 6</td>
    </tr>
  </table>
  <table>
    <tr>
      <th>I Want You</th>
      <th>header value</th>
      <th>header value</th>
    </tr>
    <tr>
      <td>I Added This value 1</td>
      <td>value 2</td>
      <td>value 3</td>
    </tr>
    <tr>
      <td>I Added This value 4</td>
      <td>value 5</td>
      <td>value 6</td>
    </tr>
  </table>
</body>

説明:

  1. テンプレート #1 - アイデンティティ テンプレート- すべてをそのままコピーします。このテンプレートは、後続のテンプレートで上書きされます。

  2. テンプレート #2 - このテンプレートは、XPath のドゥージーに一致します<td>。位置が "I Want You" の値を持つ<th>要素 (最も近い "header" 内) と一致する要素内に含まれるすべてのテキストを取得します。<tr>このテキストについては、「I added This」と元のテキストを連結した新しい値を返します。

于 2012-06-21T21:00:37.870 に答える
0

このスタイルシート (XSLT 1.0 でも動作します) ...

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

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

  <xsl:template match="td">
  <xsl:variable name="col" select="count(preceding-sibling::td)+1" /> 
  <xsl:copy>
   <xsl:apply-templates select="@*"/>
   <xsl:if test="contains(../../tr[1]/th[$col], 'I Want You')">
     <xsl:value-of select="'I Added This '" /> 
   </xsl:if>  
   <xsl:apply-templates select="node()"/>
 </xsl:copy>
</xsl:template>

</xsl:stylesheet>

...サンプル入力に適用すると、必要な出力が生成されます。

ティムの答えは間違っているか、少なくとも一貫して正しいとは言えません。position() 関数はトリッキーです。問題は、position() に空白ノードや、microdata 形式などの html テーブルにある可能性があるその他のテーブル以外の要素が含まれていることです。ヘッダー行の空白がデータ行と異なる場合、Tim C のソリューションは機能しません。count() が必要です。

意図的に lower-case() ビットを省略しました。テストの些細な詳細は OP に任せます。

また、さらに単純な XSLT 2.0 ソリューションもあります ...

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

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

  <xsl:template match="td[
     for $col in position() return
       contains(../../tr[1]/th[$col], 'I Want You')]">
  <xsl:copy>
   <xsl:apply-templates select="@*"/>
   <xsl:value-of select="'I Added This '" /> 
   <xsl:apply-templates select="node()"/>
 </xsl:copy>
</xsl:template>

</xsl:stylesheet>
于 2012-06-20T15:16:20.790 に答える