按子节点序列进行高级分组

时间:2014-05-27 17:04:18

标签: xml xslt xslt-2.0 xslt-grouping

我试图遍历每个"目的地"根据他们的旅行分组' IDS。也就是说,包含完全相同行程(由其id确定)的所有目的地应该是一次迭代。

在下面的示例XML中,两个第一个目的地每个包含四次旅行。目的地的四次旅行" 1" /"巴哈马"与目的地相同的四次旅行" 2" /"夏威夷"。第三个目的地包含另一组旅行。我想在这里进行两次迭代:第一次迭代包含目标" 1"和" 2" (因为目的地的所有旅行" 1"在目的地" 2"反过来),第二次迭代包含目的地" 3"。

如果 目的地" 2"没有id =" 4"的行程,我希望它能进行三次迭代:每个目的地一次,因为它们都不包含完全相同的行程。

数据的结构或多或少与此类似,但在另一个上下文和更多数据中。 Sooo,不要过分关注数据的结构。遗憾的是,改变结构不是一种选择,因为我无法控制数据。

<destinations>
  <destination>
    <key>1</key>
    <location>Bahamas</location>
    <tags>
      <tag>summer</tag>
      <tag>beach</tag>
      <tag>surfing</tag>
    </tags>
    <easy-trips>
      <trip>
        <id>1</id>
      </trip>
      <trip>
        <id>2</id>
      </trip>
    </easy-trips>
    <experienced-trips>
      <trip>
        <id>3</id>
      </trip>
      <trip>
        <id>4</id>
      </trip>
    <experienced-trips>
  </destination>
  <destination>
    <key>2</key>
    <location>Hawaii</location>
    <tags>
      <tag>summer</tag>
      <tag>beach</tag>
      <tag>surfing</tag>
    </tags>
    <easy-trips>
      <trip>
        <id>1</id>
      </trip>
      <trip>
        <id>2</id>
      </trip>
    </easy-trips>
    <experienced-trips>
      <trip>
        <id>3</id>
      </trip>
      <trip>
        <id>4</id>
      </trip>
    <experienced-trips>
  </destination>
  <destination>
    <key>3</key>
    <location>Rio</location>
    <tags>
      <tag>big city life</tag>
      <tag>samba</tag>
    </tags>
    <easy-trips>
      <trip>
        <id>8</id>
      </trip>
      <trip>
        <id>9</id>
      </trip>
    </easy-trips>
    <experienced-trips>
      <trip>
        <id>10</id>
      </trip>
      <trip>
        <id>11</id>
      </trip>
    <experienced-trips>
  </destination>
</destinations>

到目前为止我尝试了什么

<xsl:for-each-group select="/destinations/destination" group-by="current()//id">

当我在每个目的地只有一次旅行时,在开始时表现出了良好的承诺。添加更多行程时,这将无效。在上面的示例中,它将前两个目的地作为一个组循环四次(每个目标共有一个),然后继续前往目的地&#34; 3&#34; /&#34;里约&#34;。

还考虑了Muenchian方法,但似乎没有比#34; for-each-group&#34;尝试。

有解决方案吗?

有关如何解决此问题的任何想法都是 高度 赞赏!提前,谢谢你的时间和帮助! : - )

---编辑---

向示例XML(标记)添加了更多数据。作为数据结构的相关性,包含完全相同行程的目标将始终具有相同的标记。像目的地&#34; 1&#34;和&#34; 2&#34;在示例中。

所需的输出

<tagCollections>
  <tagCollection>
    <tag>summer</tag>
    <tag>beach</tag>
    <tag>surfing</tag>
  </tagCollection>
  <tagCollection>
    <tag>big city life</tag>
    <tag>samba</tag>
  </tagCollection>
</tagCollections>

1 个答案:

答案 0 :(得分:0)

我认为您希望使用<xsl:for-each-group select="/destinations/destination" group-by="string-join(.//id, '+')">之类的代码在Xslt 2.0中计算复合分组键。我使用的加号只是一个例子,使用从未在id值中使用的任何字符。

当前的建议假定id是有序的,如果没有,你需要首先编写一个函数来执行排序,然后在group-by中调用。

我添加了一个XSLT 3.0示例(可以与Saxon 9的商业版本一起运行),它展示了如何使用函数对序列进行排序以及如何在group-by中使用它:

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:mf="http://example.com/mf"
  exclude-result-prefixes="xs mf">

<xsl:output indent="yes"/>

<xsl:function name="mf:sort">
  <xsl:param name="input-sequence" as="item()*"/>
  <xsl:perform-sort select="$input-sequence">
    <xsl:sort select="."/>
  </xsl:perform-sort>
</xsl:function>

<xsl:template match="destinations">
  <tagCollections>
    <xsl:for-each-group select="/destinations/destination" 
        group-by="string-join(mf:sort(.//id/xs:integer(.))!string(), '+')">
      <tagCollection>
        <xsl:copy-of select="tags/tag"/>
      </tagCollection>
    </xsl:for-each-group>
  </tagCollections>
</xsl:template>

</xsl:stylesheet>

应用于输入样本

<destinations>
  <destination>
    <key>1</key>
    <location>Bahamas</location>
    <tags>
      <tag>summer</tag>
      <tag>beach</tag>
      <tag>surfing</tag>
    </tags>
    <easy-trips>
      <trip>
        <id>1</id>
      </trip>
      <trip>
        <id>2</id>
      </trip>
    </easy-trips>
    <experienced-trips>
      <trip>
        <id>3</id>
      </trip>
      <trip>
        <id>4</id>
      </trip>
    </experienced-trips>
  </destination>
  <destination>
    <key>2</key>
    <location>Hawaii</location>
    <tags>
      <tag>summer</tag>
      <tag>beach</tag>
      <tag>surfing</tag>
    </tags>
    <easy-trips>
      <trip>
        <id>4</id>
      </trip>
      <trip>
        <id>3</id>
      </trip>
    </easy-trips>
    <experienced-trips>
      <trip>
        <id>2</id>
      </trip>
      <trip>
        <id>1</id>
      </trip>
    </experienced-trips>
  </destination>
  <destination>
    <key>3</key>
    <location>Rio</location>
    <tags>
      <tag>big city life</tag>
      <tag>samba</tag>
    </tags>
    <easy-trips>
      <trip>
        <id>8</id>
      </trip>
      <trip>
        <id>9</id>
      </trip>
    </easy-trips>
    <experienced-trips>
      <trip>
        <id>10</id>
      </trip>
      <trip>
        <id>11</id>
      </trip>
    </experienced-trips>
  </destination>
</destinations>

我得到了输出

<tagCollections>
   <tagCollection>
      <tag>summer</tag>
      <tag>beach</tag>
      <tag>surfing</tag>
   </tagCollection>
   <tagCollection>
      <tag>big city life</tag>
      <tag>samba</tag>
   </tagCollection>
</tagCollections>

使用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"
  xmlns:mf="http://example.com/mf"
  exclude-result-prefixes="xs mf">

<xsl:output indent="yes"/>

<xsl:function name="mf:sort">
  <xsl:param name="input-sequence" as="item()*"/>
  <xsl:perform-sort select="$input-sequence">
    <xsl:sort select="."/>
  </xsl:perform-sort>
</xsl:function>

<xsl:template match="destinations">
  <tagCollections>
    <xsl:for-each-group select="/destinations/destination" 
        group-by="string-join(for $n in mf:sort(.//id/xs:integer(.)) return string($n), '+')">
      <tagCollection>
        <xsl:copy-of select="tags/tag"/>
      </tagCollection>
    </xsl:for-each-group>
  </tagCollections>
</xsl:template>

</xsl:stylesheet>