将两个XSLT模板应用于同一元素的最佳方法

时间:2013-11-27 02:41:38

标签: xml templates xslt

我有这样的XML:

<data>
    <admin id="abc">
        <type>one</type>
        <!-- <detail/> might appear here but the content is unrelated to <detail/> below -->
    </admin>
    <detail id="def">
        <name>Bob</name>
    </detail>
</data>

然后我有这样的XSLT:

<xsl:template match="/data/admin">
    Admin content
</xsl:template>

<xsl:template match="/data/detail">
    Some content to appear before ALL types.  This id is <xsl:value-of select="@id"/>
    <xsl:choose>
        <xsl:when test="/data/admin/type='one'>
            Content for type One only.  Your name is <xsl:value-of select="name"/>
        </xsl:when>
        <xsl:when test="/data/admin/type='two'>
            Content for type Two only.  Your name is still <xsl:value-of select="name"/>
        </xsl:when>
        <xsl:otherwise>
            Content if the type is not set or set to an unknown value
        </xsl:otherwise>
    </xsl:choose>
    Some content to appear after ALL types.  This id is <xsl:value-of select="@id"/>
</xsl:template>

因为<xsl:choose/>非常大,我想将其拆分为不同的文件 - 每个条件一个 - 并且<xsl:include/>将它们分成主文档。

我遇到的问题是<xsl:include/>要求每个包含的文件包含模板,我不能包含原始数据。所以我留下了这些选择:

  • 在每个包含的文件中,使用<xsl:template match="/data/detail"/> - 然后覆盖上面的父模板,因此我必须将“for ALL types”的数据复制并粘贴到每个文件中。由于数据重复,这并不好。
  • 在每个包含的文件中,使用<xsl:template match="/data/admin/type='one'"/>之类的内容,然后调用<xsl:apply-templates select="/data/admin/type"/>。这不起作用,因为在“管理内容”模板中,我将有一个我不想要的子<type/>节点的无意模板实例。
  • 将每个内容块定义为命名模板,然后在我的<xsl:choose/>内按名称调用每个内容。当XSLT能够在没有明确命名的情况下选择正确的模板时,这似乎有些不整洁。

有没有更简洁的方法可以实现?

输出应如下所示:

Admin content
Some content to appear before ALL types.  This id is def
Content for type One only.  Your name is Bob
Some content to appear after ALL types.  This id is def

2 个答案:

答案 0 :(得分:1)

你可以有两个不同的模板匹配两个不同的XPath,每个类型一个,如下所示:

<xsl:template match="/data[admin/type='one']/detail">
    <!-- Type one specific transform -->
</xsl:template>
<xsl:template match="/data[admin/type='two']/detail">
    <!-- Type two specific transform -->
</xsl:template>

这些模板中的每一个都可以在他们自己的文件中,并且每个文件都包含在内。

答案 1 :(得分:1)

这是@Lego Stormtroopr给出的答案的延伸。他建议从两个模板开始,每个模板匹配一种类型:

<xsl:template match="/data[admin/type='one']/detail">
 <!-- Type one specific transform -->
</xsl:template>

<xsl:template match="/data[admin/type='two']/detail">
 <!-- Type two specific transform -->
</xsl:template>

现在对于content to appear for ALL types,这里有两种可能性。如果出现的所有类型的部分都是 content ,请使用全局变量并将其存储在那里:

<xsl:variable name="var1">
 <!--Content for all types-->
</xsl:variable>

<xsl:template match="/data[admin/type='one']/detail">
 <!-- content for ALL types example: -->
 <xsl:value-of select="$var1"/>
 <!-- Go on with type specific content -->
</xsl:template>

<xsl:template match="/data[admin/type='two']/detail">
 <!-- content for ALL types example: -->
 <xsl:value-of select="$var1"/>
 <!-- Go on with type specific content -->
</xsl:template>

如果您需要捕获detail内的输入元素,只需为它们定义另一个模板,让apply-templates找到它们。

<xsl:template match="/data[admin/type='one']/detail">
 <!-- content for ALL types example: -->
 <xsl:apply-templates/>
 <!-- Go on with type specific content -->
</xsl:template>

<xsl:template match="/data[admin/type='two']/detail">
 <!-- content for ALL types example: -->
 <xsl:apply-templates/>
 <!-- Go on with type specific content -->
</xsl:template>

<xsl:template match="*[parent::detail]">
 <!--template content-->
</xsl:template>