这是我在一个生成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意义上),但我在网上找不到任何例子。任何建议都非常欢迎!
答案 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('
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
同样,我们会在没有图书或者缺少这两种项目时获得想要的结果。
请注意:
简洁(没有模式,没有明确的条件指示)。
模板的最小数量(只是一个错误处理模板,无论可能的不同错误类型的数量如何)。
对于给定类型的处理,只有一个<xsl:apply-templates>
- 不需要特殊的额外<xsl:apply-templates>
进行错误处理。
灵活性,可扩展性,可维护性 - 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('
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('
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>