2

これは、XSL-FO を生成するより複雑な XSLT 1.0 スタイルシートで私が抱えているマッチングの問題の簡単な例です。

<Library>0 個以上の<Item>ノードを含む可能性があるこの入力 XML を考えると、

<Library>
  <Item type="magazine" title="Rum"/>
  <Item type="book" title="Foo" author="Bar"/>
  <Item type="book" title="Fib" author="Fub"/>
  <Item type="magazine" title="Baz"/>
</Library>

そして、この XSLT:

<xsl:template match="Library">
  <xsl:apply-templates select="Item[@type='Magazine']/>
  <!-- How to call "NoMagazines" from here? -->
  <xsl:apply-templates select="Item[@type='Book']/>
  <!-- How to call "NoBooks" from here? -->
</xsl:template>

<xsl:template match="Item[@type='book']">
  <!-- do something with books -->
</xsl:template>

<xsl:template match="Item[@type='magazine']">
  <!-- do something with magazines -->
</xsl:template>

<!-- how to call this template? -->
<xsl:template name="NoBooks">
  Sorry, No Books!
</xsl:template>

<!-- how to call this template? -->
<xsl:template name="NoMagazines">
  Sorry, No Magazines!
</xsl:template>

代わりの「ごめんなさい、ダメ!」を作りたいです。タイプ [whatever] のノードLibraryがない場合のテンプレートからのメッセージ。Item

これまでのところ、私が行った唯一の(醜い)解決策は、子ノードを変数にタイプ別に選択し、変数をテストしてから、変数にノードが含まれている場合はテンプレートを適用するか、適切な「一致しない」名前付きテンプレートを呼び出すことです。変数は空です (ノードが選択されていない場合、test="$foo" は false を返すと想定していますが、まだ試していません):

<xsl:template match="Library">
  <xsl:variable name="books" select="Items[@type='book']"/>

  <xsl:choose>
    <xsl:when test="$books">
      <xsl:apply-templates select="$books"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:call-template name="NoBooks"/>
    </xsl:otherwise>
  </xsl:choose>

  <xsl:variable name="magazines" select="Items[@type='magazine']"/>

  <xsl:choose>
    <xsl:when test="$magazines">
      <xsl:apply-templates select="$magazines"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:call-template name="NoMagazines"/>
    </xsl:otherwise>
  </xsl:choose>

</xsl:template>

これは (GoF の意味での) XSLT デザイン パターンに違いないと思いましたが、オンラインで例を見つけることができませんでした。どんな提案も大歓迎です!

4

5 に答える 5

3

Dimitreのソリューションの変形として、おそらく次のようなものです:

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

 <my:errorObjects>
  <noItem type="book">No Books</noItem>
  <noItem type="magazine">No Magazines</noItem>
 </my:errorObjects>

 <xsl:variable name="vErrorObjects" select=
  "document('')/*/my:errorObjects"/>


 <xsl:template match="/*">
  <xsl:apply-templates select="(.|$vErrorObjects)/*[@type='magazine']/>   
  <xsl:apply-templates select="(.|$vErrorObjects)/*[@type='book']"/>
 </xsl:template>

 <xsl:template match="Item">
  <xsl:value-of select="concat('&#xA;Type: ', @type, ', title: ', @title)"/>
 </xsl:template>

 <xsl:template match="noItem">
  <xsl:if test="last() = 1">
    Sorry: <xsl:value-of select="."/>
  </xsl:if>
 </xsl:template>

</xsl:stylesheet>
于 2012-05-16T07:21:41.850 に答える
3

私はこのように行きます

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

 <my:errorObjects>
  <noBook>No Books</noBook>
  <noMagazine>No Magazines</noMagazine>
 </my:errorObjects>

 <xsl:variable name="vErrorObjects" select=
  "document('')/*/my:errorObjects"/>


 <xsl:template match="/*">
  <xsl:apply-templates select=
  "*[@type='magazine']
  | $vErrorObjects[not(current()[*[@type='magazine']])]/noMagazine"/>

  <xsl:apply-templates select=
  "*[@type='book']
  | $vErrorObjects[not(current()[*[@type='book']])]/noBook"/>
 </xsl:template>

 <xsl:template match="Item">
  <xsl:value-of select="concat('&#xA;Type: ', @type, ', title: ', @title)"/>
 </xsl:template>

 <xsl:template match="my:errorObjects/*">
  Sorry: <xsl:value-of select="."/>
 </xsl:template>
</xsl:stylesheet>

この変換が提供された XML ドキュメントに適用されると、次のようになります。

<Library>
    <Item type="magazine" title="Rum"/>
    <Item type="book" title="Foo" author="Bar"/>
    <Item type="book" title="Fib" author="Fub"/>
    <Item type="magazine" title="Baz"/>
</Library>

必要な (通常の) 結果が生成されます。

Type: magazine, title: Rum
Type: magazine, title: Baz
Type: book, title: Foo
Type: book, title: Fib

次の XML ドキュメント (雑誌なし) に変換が適用された場合:

<Library>
    <Item type="XXXXX" title="Rum"/>
    <Item type="book" title="Foo" author="Bar"/>
    <Item type="book" title="Fib" author="Fub"/>
    <Item type="YYYYY" title="Baz"/>
</Library>

ここでも正しい結果が生成されます (雑誌の場合はエラー メッセージ、書籍の場合は通常の結果)。

  Sorry: No Magazines
Type: book, title: Foo
Type: book, title: Fib

同様に、本がない場合、または両方の種類のアイテムが欠落している場合に、必要な結果が得られます

注意してください:

  1. シンプルさ (モードなし、明示的な条件付き命令なし)。

  2. テンプレートの最小数(考えられるさまざまなエラー タイプの数に関係なく、1 つのエラー処理テンプレートのみ)。

  3. <xsl:apply-templates>特定のタイプの処理に対して1 つだけ<xsl:apply-templates>です。エラー処理用に特別な追加は必要ありません。

  4. 柔軟性、拡張性、保守性-- はerrorObjects独自の別のファイルに常駐できます。

于 2012-05-16T02:34:16.267 に答える
3

以下の解決策はより簡潔ですが、同じ原則です。関数を使用してcount()雑誌アイテムが存在するかどうかを判断し、存在しない場合はNoMagazinesテンプレートを呼び出します。

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

    <xsl:template match="Library">
        <library>

            <!-- magazines -->
            <xsl:apply-templates select="Item[@type='magazine']"/>
            <xsl:if test="count(Item[@type='magazine']) = 0">
                <xsl:call-template name="NoMagazines"/>
            </xsl:if>

            <!-- books -->
            <!-- ... -->

        </library>      
    </xsl:template>

    <xsl:template match="Item[@type='magazine']">
        <magazine>...</magazine>
    </xsl:template>

    <xsl:template name="NoMagazines">
        <noMagazines/>
    </xsl:template>

</xsl:stylesheet>
于 2012-05-15T21:28:21.013 に答える
-1

これは実際には独創的なアイデアではありません。これは、Michaelのソリューションを微調整しただけです。document()関数はXSLT 1.0にとって少し問題があるように思われるので、私はそれを避けようとしました。

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:msxsl="urn:schemas-microsoft-com:xslt">
 <xsl:output method="text"/>

 <xsl:variable name="vErrorObjects">
  <Books/><Magazines/>
 </xsl:variable>

 <xsl:template match="/Library">
  <xsl:apply-templates select="*[@type='magazine'],$vErrorObjects/Magazines"/>   
  <xsl:apply-templates select="*[@type='book'    ],$vErrorObjects/Books"    />
 </xsl:template>

 <xsl:template match="Item">
  <xsl:value-of select="concat('&#xA;Type: ', @type, ', title: ', @title)"/>
 </xsl:template>

 <xsl:template match="Magazines|Books">
  <xsl:if test="last() = 1">
    Sorry: No <xsl:value-of select="local-name()"/>
  </xsl:if>
 </xsl:template>

</xsl:stylesheet>
于 2012-05-16T15:36:36.837 に答える
-1

「雑誌なし」と「本なし」に明示的に一致するテンプレートを使用しない理由:

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

<xsl:template match="Library">
  <xsl:apply-templates select="Item" />
  <!-- Apply no[books|magazines] from here -->
  <xsl:apply-templates select="." mode="noresults" />
</xsl:template>

<xsl:template match="Item[@type='book']">
  <!-- do something with books -->
</xsl:template>

<xsl:template match="Item[@type='magazine']">
  <!-- do something with magazines -->
</xsl:template>

<xsl:template match="Library[not(Item[@type='book'])]" mode="noresults" >
  Sorry, No Books!
</xsl:template>

<xsl:template match="Library[not(Item[@type='magazine'])]" mode="noresults" >
  Sorry, No Magazines!
</xsl:template>

</xsl:stylesheet>
于 2012-05-15T23:41:29.997 に答える