我想在apply-templates不匹配时调用命名模板

时间:2012-05-15 20:41:39

标签: xslt

这是我在一个生成XSL-FO的更复杂的XSLT 1.0样式表中遇到的匹配问题的一个简单示例。

鉴于此输入XML,<Library>可能包含零个或多个<Item>个节点,

<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>

我想制作替代品'抱歉,不[不管]!当没有[{1}}类型的节点时,来自Library模板的消息。

到目前为止,我所做的唯一(丑陋)解决方案是按类型将子节点选择为变量,测试变量,然后如果变量包含节点则应用模板,或调用适当的“不匹配”命名模板如果变量为空(我假设test =“$ foo”将返回false,如果没有选择节点,我还没有尝试过):

Item

我认为这必须是一个XSLT设计模式(在GoF意义上),但我在网上找不到任何例子。任何建议都非常欢迎!

5 个答案:

答案 0 :(得分: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>

答案 1 :(得分: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. 模板的最小数量(只是一个错误处理模板,无论可能的不同错误类型的数量如何)。

  3. 对于给定类型的处理,只有一个<xsl:apply-templates> - 不需要特殊的额外<xsl:apply-templates>进行错误处理。

  4. 灵活性,可扩展性,可维护性 - errorObjects可以驻留在各自独立的文件中。

答案 2 :(得分: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>

答案 3 :(得分:-1)

为什么不使用与&#34; no magazine&#34;匹配的模板?和#34;没有书&#34;明确地:

<?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>

答案 4 :(得分:-1)

这不是一个真正的想法。这只是迈克尔解决方案的一个调整。对于XSLT 1.0来说,document()函数似乎有点问题,所以我试图避免它。

<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>