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

StackOverflow https://stackoverflow.com/questions/807578

  •  03-07-2019
  •  | 
  •  

给定以下 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)]

但我不知道如何获得第一个。

有帮助吗?

解决方案

获取每个“中的第一个和最后一个出现(文档顺序)<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“) 查看…
    • …如果它具有相同的唯一 ID (generate-id()) 作为返回的第一个节点 key() 或返回的最后一个节点 key()
    • 在哪里 key('ContainerGroupByVal', val) 返回一组 <container> 匹配当前节点 <val>
    • 如果唯一 ID 匹配,则将该节点包含在选择中
  • <xsl:for-each> 执行输出。它也可能是一个 <xsl:apply-templates>.

编辑#2:正如 Dimitre Novaatchev 在评论中正确指出的那样,您应该谨慎使用“//" 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。

其他提示

基本 XPath

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

这里有一套更详细的 XPath功能

<强> 予。 XSLT 1.0

基本上与Tomalak的解决方案相同,但更容易理解 它也是完整的,因此您只需要复制并粘贴XML文档和转换,然后只需按<!> QUOT;变换QUOT <!>;您最喜欢的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. 从具有相同<xsl:for-each-group>current-group()元素的整个节点列表中,我们输出组中第一个<=>元素和最后一个元素所需的数据<= >小组中的元素。
  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文档时,产生想要的结果

    <*>

    请注意

    1. 使用<=> XSLT指令。

    2. 使用<=>功能。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top