2

这是我在生成 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>

我想制作替代品“对不起,不[无论如何]!” Library当没有Item类型 [whatever] 的节点时来自模板的消息。

到目前为止,我所做的唯一(丑陋)解决方案是按类型选择子节点到变量中,测试变量,然后如果变量包含节点,则应用模板,或者调用适当的“不匹配”命名模板,如果变量为空(我假设 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>

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

4

5 回答 5

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 回答
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可以驻留在它们自己的单独文件中。

于 2012-05-16T02:34:16.267 回答
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 回答
-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 回答
-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>
于 2012-05-16T15:36:36.837 回答