为元素的每个值的第一次和最后一次出现生成上下文数据

时间:2009-04-30 15:41:33

标签: xslt xpath

给出以下xml:

<container>
    <val>2</val>
    <id>1</id>
</container>
<container>
    <val>2</val>
    <id>2</id>
</container>
<container>
    <val>2</val>
    <id>3</id>
</container>
<container>
    <val>4</val>
    <id>1</id>
</container>
<container>
    <val>4</val>
    <id>2</id>
</container>
<container>
    <val>4</val>
    <id>3</id>
</container>

我想返回类似

的内容
2 - 1
2 - 3
4 - 1
4 - 3

使用节点集我已经能够通过以下方式获得最后一次:

exsl:node-set($list)/container[not(val = following::val)]

但我无法弄清楚如何获得第一个。

3 个答案:

答案 0 :(得分:3)

要获取每个“<val>”组中的第一个和最后一个(文档顺序),您可以使用<xsl:key>,如下所示:

<xsl:stylesheet 
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
  <xsl:output method="text" />

  <xsl:key name="ContainerGroupByVal" match="container" use="val" />

  <xsl:variable name="ContainerGroupFirstLast" select="//container[
    generate-id() = generate-id(key('ContainerGroupByVal', val)[1])
    or
    generate-id() = generate-id(key('ContainerGroupByVal', val)[last()])
  ]" />

  <xsl:template match="/">
    <xsl:for-each select="$ContainerGroupFirstLast">
      <xsl:value-of select="val" />
      <xsl:text> - </xsl:text>
      <xsl:value-of select="id" />
      <xsl:value-of select="'&#10;'" /><!-- LF -->
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

编辑#1:有点解释,因为这可能不会立即显而易见:

  • <xsl:key>返回具有给定<container>的所有<val>个节点。您可以使用key()函数进行查询。
  • <xsl:variable>就是这一切发生的地方。它读作:
    • 为文档中的每个<container>节点(“//container”)检查...
    • ...如果它与generate-id()返回的第一个节点或key()返回的最后一个节点具有相同的唯一ID(key()
    • 其中key('ContainerGroupByVal', val)返回与当前<container>
    • 匹配的<val>个节点集
    • 如果唯一ID匹配,请在选择中包含节点
  • <xsl:for-each>执行输出。它也可以是<xsl:apply-templates>

编辑#2:正如Dimitre Novatchev在评论中正确指出的那样,你应该警惕使用“//”XPath简写。如果你可以避免它,一定要这样做 - 部分是因为它可能选择你不想要的节点,主要是因为它比一个更具体的XPath表达式更慢。例如,如果您的文档如下:

<containers>
  <container><!-- ... --></container>
  <container><!-- ... --></container>
  <container><!-- ... --></container>
</containers>

然后你应该使用“/containers/container”或“/*/container”代替“//container”。


编辑#3:上述的另一种语法是:

<xsl:stylesheet 
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
  <xsl:output method="text" />

  <xsl:key name="ContainerGroupByVal" match="container" use="val" />

  <xsl:variable name="ContainerGroupFirstLast" select="//container[
    count(
        .
      | key('ContainerGroupByVal', val)[1]
      | key('ContainerGroupByVal', val)[last()]
    ) = 2
  ]" />

  <xsl:template match="/">
    <xsl:for-each select="$ContainerGroupFirstLast">
      <xsl:value-of select="val" />
      <xsl:text> - </xsl:text>
      <xsl:value-of select="id" />
      <xsl:value-of select="'&#10;'" /><!-- LF -->
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

说明:XPath联合运算符“|”将其参数组合到节点集中。根据定义,节点集不能包含重复节点 - 例如:“. | . | .”将创建一个仅包含一个节点(当前节点)的节点集。

这意味着,如果我们从当前节点(“。”),“key(…)[1]”节点和“key(…)[last()]”节点创建联合​​节点集,它的节点数将为2如果(且仅当)当前节点等于其他两个节点之一,则在所有其他情况下,计数将为3.

答案 1 :(得分:1)

基本XPath

//container[position() = 1]  <- this is the first one
//container[position() = last()]  <- this is the last one

这里有一组更详细的XPath functions

答案 2 :(得分:1)

<强> 予。 XSLT 1.0

基本上与Tomalak的解决方案相同,但更容易理解 它也是完整的,因此您只需要复制并粘贴XML文档和转换,然后只需按您最喜欢的XSLT IDE的“转换”按钮

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

    <xsl:key name="kContByVal" match="container"
     use="val"/>

    <xsl:template match="/*">
      <xsl:for-each select=
       "container[generate-id()
                 =
                  generate-id(key('kContByVal',val)[1])
                 ]
       ">

       <xsl:variable name="vthisvalGroup"
        select="key('kContByVal', val)"/>

       <xsl:value-of select=
        "concat($vthisvalGroup[1]/val,
              '-',
              $vthisvalGroup[1]/id,
              '&#xA;'
              )
      "/>
       <xsl:value-of select=
        "concat($vthisvalGroup[last()]/val,
              '-',
              $vthisvalGroup[last()]/id,
              '&#xA;'
              )
        "/>
      </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

将此转换应用于最初提供的XML文档(编辑为格式良好)时:

<t>
    <container>
        <val>2</val>
        <id>1</id>
    </container>
    <container>
        <val>2</val>
        <id>2</id>
    </container>
    <container>
        <val>2</val>
        <id>3</id>
    </container>
    <container>
        <val>4</val>
        <id>1</id>
    </container>
    <container>
        <val>4</val>
        <id>2</id>
    </container>
    <container>
        <val>4</val>
        <id>3</id>
    </container>
</t>

生成了想要的结果

2-1
2-3
4-1
4-3

请注意

  1. 我们使用Muenchian方法进行分组,为container具有相同值的每组此类元素找到一个val元素。
  2. 从具有相同containerval元素的整个节点列表中,我们输出组中第一个container元素的必需数据以及该组中的最后一个container元素。
  3. <强> II。 XSLT 2.0

    此转化

    <xsl:stylesheet version="2.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:xs="http://www.w3.org/2001/XMLSchema">
        <xsl:output method="text"/>
    
        <xsl:template match="/*">
          <xsl:for-each-group select="container"
               group-by="val">
            <xsl:for-each select="current-group()[1], current-group()[last()]">
              <xsl:value-of select=
               "concat(val, '-', id, '&#xA;')"/>
            </xsl:for-each>
        </xsl:for-each-group>
        </xsl:template>
    </xsl:stylesheet>
    

    应用于上述相同的XML文档时,产生想要的结果

    2-1
    2-3
    4-1
    4-3
    

    请注意

    1. 使用<xsl:for-each-group> XSLT指令。

    2. 使用current-group()功能。