xml 使用 XSLT Apply-Templates 有条件地选择节点

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3982290/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-06 13:21:40  来源:igfitidea点击:

Using XSLT Apply-Templates to conditionally select nodes

xmlxsltxpath

提问by Msencenb

Let's say I have an xml document like this:

假设我有一个像这样的 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>

Now I want to be able to select all the plays with Will Smith as an actor and reformat it into something like this:

现在我希望能够选择威尔史密斯作为演员的所有戏剧并将其重新格式化为如下所示:

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

I only want to use apply-templates.. No xsl:if or for each loops (I have contrived this example as a simpler version of what I'm doing so you can help me understand how to use xpath within a match statement)

我只想使用 apply-templates .. 没有 xsl:if 或 for each 循环(我将这个例子设计为我正在做的更简单的版本,所以你可以帮助我理解如何在 match 语句中使用 xpath)

Here is what I have so far:

这是我到目前为止所拥有的:

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

Basically I am just unsure of how to filter out nodes using XPath in the match attribute of the template. Any help would be great!

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

回答by ckarras

The condition should be on xsl:apply-templates instead of xsl:template:

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

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

In your solution, you are transforming ALL <play> nodes. For play nodes that match the condition, your template is applied. But for those that don't match the condition, a default template ("identity transform") is applied instead.

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

Alternatively, you could keep the condition on xsl:template match, but add another template for <play> that do not match the condition, to transform those <play> into nothing:

或者,您可以在 xsl:template 匹配上保留条件,但为 <play> 添加另一个与条件不匹配的模板,将那些 <play> 转换为空:

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

回答by Dimitre Novatchev

I. Probably the most efficient XSLT 1.0 solution:

I. 可能是最高效的 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>

when this transformation is applied on the provided XML document:

当此转换应用于提供的 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>

the wanted result is produced:

产生了想要的结果

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

Do note:

请注意

  1. Efficiency is achieved by using keysboth for all plays in which Mell Gibson took part and for all actors that took part in a given (titled) play.

  2. Even if a play title with Mel Gibson were listed more than once (due to accidental error, perhaps...) it will be listed only once in the result.

  1. 通过对梅尔吉布森参与的所有戏剧和参与给定(有标题)戏剧的所有演员使用密钥来实现效率

  2. 即使 Mel Gibson 的游戏标题被列出不止一次(由于意外错误,也许......)它也只会在结果中列出一次

II. A simple and efficient XSLT 2.0 solution:

二、一个简单高效的 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>

回答by Dimitre Novatchev

This stylesheet:

这个样式表:

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

Output:

输出:

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