【问题标题】:Using XSLT Apply-Templates to conditionally select nodes使用 XSLT Apply-Templates 有条件地选择节点
【发布时间】:2010-10-20 21:03:08
【问题描述】:

假设我有一个这样的 xml 文档:

<director>
    <play>
        <t>Nutcracker</t>
        <a>Tom Cruise</a>
    </play>
    <play>
        <t>Nutcracker</t>
        <a>Robin Williams</a>
    </play>
    <play>
        <t>Grinch Stole Christmas</t>
        <a>Will Smith</a>
    </play>
    <play>
        <t>Grinch Stole Christmas</t>
        <a>Mel Gibson</a>
    </play>
</director>

现在我希望能够选择 Will Smith 作为演员的所有戏剧并将其重新格式化为如下内容:

<Plays>
    <Play title="Grinch Stole Christmas">
       <star>Will Smith</star>
       <star>Mel Gibson</star>
    </Play>
</Plays>

我只想使用 apply-templates.. 没有 xsl:if 或 for each 循环(我设计了这个示例作为我正在做的更简单的版本,因此您可以帮助我了解如何在匹配中使用 xpath声明)

这是我目前所拥有的:

<?xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
        <xsl:template match="/director">
                <Plays>
                <xsl:apply-templates select="play"/>
                </Plays>
        </xsl:template>

        <xsl:template match="play[a='Will Smith']">
                <play title="{data(t)[1]}">
                <xsl:apply-templates select="a"/>
                </play>
        </xsl:template>

        <xsl:template match="a">
                <star>
                <xsl:value-of select="."/>
                </star>
        </xsl:template>
</xsl:stylesheet>

基本上我只是不确定如何在模板的 match 属性中使用 XPath 过滤掉节点。任何帮助都会很棒!

【问题讨论】:

    标签: xml xslt xpath


    【解决方案1】:

    条件应该是 xsl:apply-templates 而不是 xsl:template:

    <Plays>
      <xsl:apply-templates select="play[a='Will Smith']">"/>
    </Plays>
    

    在您的解决方案中,您正在转换所有 节点。 对于符合条件的播放节点,将应用您的模板。但对于那些不符合条件的,则应用默认模板(“身份转换”)。

    或者,您可以保持 xsl:template 匹配的条件,但为 添加另一个不匹配条件的模板,以将这些 转换为空:

        <xsl:template match="play[a='Will Smith']">
          <play title="{data(t)[1]}">
            <xsl:apply-templates select="a"/>
          </play>
        </xsl:template>
    
        <xsl:template match="play">
        </xsl:template>
    

    【讨论】:

    • 啊哈!您刚刚回答了一个我没有看到任何教程解释的问题。我在哪里可以找到有关此“默认模板”的信息?
    【解决方案2】:

    我。可能是最高效的 XSLT 1.0 解决方案

    <xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
     <xsl:strip-space elements="*"/>
    
     <xsl:key name="kWSPlayByTitle" match="play[a='Will Smith']"
      use="t"/>
    
     <xsl:key name="kActorByTitle" match="a"
      use="../t"/>
    
     <xsl:template match="/">
      <Plays>
        <xsl:apply-templates select=
        "*/play[generate-id()
               =
                generate-id(key('kWSPlayByTitle',t)[1])
               ]"/>
      </Plays>
     </xsl:template>
    
     <xsl:template match="play">
      <Play title="{t}">
       <xsl:apply-templates select="key('kActorByTitle',t)"/>
      </Play>
     </xsl:template>
    
     <xsl:template match="a">
      <star><xsl:value-of select="."/></star>
     </xsl:template>
    </xsl:stylesheet>
    

    当此转换应用于提供的 XML 文档时

    <director>
        <play>
            <t>Nutcracker</t>
            <a>Tom Cruise</a>
        </play>
        <play>
            <t>Nutcracker</t>
            <a>Robin Williams</a>
        </play>
        <play>
            <t>Grinch Stole Christmas</t>
            <a>Will Smith</a>
        </play>
        <play>
            <t>Grinch Stole Christmas</t>
            <a>Mel Gibson</a>
        </play>
    </director>
    

    产生想要的结果

    <Plays>
       <Play title="Grinch Stole Christmas">
          <star>Will Smith</star>
          <star>Mel Gibson</star>
       </Play>
    </Plays>
    

    请注意

    1. 效率是通过使用键实现的,无论是梅尔吉布森参与的所有戏剧还是参与给定(标题)戏剧的所有演员。

    2. 即使与 Mel Gibson 合作的游戏名称被多次列出(可能是由于意外错误...),它也只会在结果中列出一次

    二。一个简单高效的 XSLT 2.0 解决方案

    <xsl:stylesheet version="2.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
    
     <xsl:template match="/*">
      <Plays>
        <xsl:for-each-group select="play[a='Mel Gibson']"
              group-by="t">
          <xsl:apply-templates select="."/>
        </xsl:for-each-group>
      </Plays>
     </xsl:template>
    
     <xsl:template match="play">
      <Play title="{t}">
       <xsl:for-each-group select="../play[t = current()/t]/a"
            group-by=".">
         <xsl:apply-templates select="."/>
       </xsl:for-each-group>
      </Play>
     </xsl:template>
    
     <xsl:template match="a">
      <star>
        <xsl:value-of select="."/>
      </star>
     </xsl:template>
    </xsl:stylesheet>
    

    【讨论】:

    • 我从来没有听说过钥匙……但这似乎是最有效的。谢谢
    • @Msencenb:请注意,XSLT 2.0 解决方案根本不使用密钥。他们的角色由&lt;xsl:for-each-group&gt; 扮演。但是,当然,即使在 XSLT 2.0 中,知道如何使用键也没有什么坏处,而且确实很有用。
    • @Dimitre:我没有使用两个键,因为这个表达式 "*/play[generate-id()...] 无论如何都会遍历所有 play 元素。
    • @Alejandro:也许你是对的。但是,如果有其他处理可以使此密钥有用,那么您显然赢了。也许在 XSLT (3+) 的未来版本中,我们将有可能检查给定内置密钥中的所有项目——这样就不需要额外的遍历了。
    • @Dimitre:是的!我在 XSLT List 中看到了这个讨论。你认为凯博士会提出这个建议吗?你知道这是否已经完成了吗?
    【解决方案3】:

    这个样式表:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:key name="kActorByTitle" match="a" use="../t"/>
        <xsl:param name="pActor" select="'Will Smith'"/>
        <xsl:template match="/">
            <Plays>
                <xsl:apply-templates select="*/play[a=$pActor]"/>
            </Plays>
        </xsl:template>
        <xsl:template match="play">
            <Play title="{t}">
                <xsl:apply-templates select="key('kActorByTitle',t)"/>
            </Play>
        </xsl:template>
        <xsl:template match="a">
            <star>
                <xsl:value-of select="."/>
            </star>
        </xsl:template>
    </xsl:stylesheet>
    

    输出:

    <Plays>
        <Play title="Grinch Stole Christmas">
            <star>Will Smith</star>
            <star>Mel Gibson</star>
        </Play>
    </Plays>
    

    【讨论】:

    • 这也很好用,并且像另一个答案一样使用键。我认为对我来说关键的一点是条件应该在 select 而不是 match 语句中
    • @Msencenb:模式匹配驱动与选择驱动是拉式与推式的问题。两者都可以。在这种情况下,因为我在 XSLT 1.0 样式表中使用了参数,所以我不得不使用推送样式(XSLT 1.0 模式中不允许参数/var 引用)
    • @Alejandro, @@Msencenb:XSLT 2.0 的匹配模式中允许变量/参数引用
    猜你喜欢
    • 2021-09-06
    • 2013-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-06
    • 1970-01-01
    相关资源
    最近更新 更多