XSL:仅通过排序拉出前5个匹配值

时间:2017-07-01 15:23:39

标签: xml xslt

我有5个需要填写的电台。这个XML输出等待客户,最近的最后一个。我需要上传XML,填充5个站中的每一个。您基本上可以通过transactionID将它们分组,并从下往上读取文件,查看每个分组的顶部位置。如果我们尚未填充VirtualDisplayId插槽,则会插入它们。

所以,你会看到我们分组交易7773324,最顶层的条目是Justin K02084,所以他获得了Id 101.然后下一个分组(7773323),Andy 2084的顶级条目也是Id 101,Justin just填充,所以这里是我的XSLT崩溃的地方。我带Andyk2084进入101,我需要跳过他,下一个条目应该是Andy2083,因为他的交易的最高入口是201,我还没有填补,所以他得到201.这个逻辑应该重复,直到所有5插槽已经填满。

我的另一个问题是,我似乎无法弄清楚如何只填充5个插槽。我的XSL一直在继续:)这很容易就是我写过的最棘手的XSL,所以最后陷入困境并且问我是否有人可以给我一些指示。

我的输出到目前为止:

<table>
   <tr><td>JUSTIN K2084</td><td>101</td></tr>
   <tr><td>ANDY K2084</td><td>101</td></tr>
   <tr><td>ANDY K2083</td><td>201</td></tr>
   <tr><td>ANDY K2082</td><td>301</td></tr>
   <tr><td>ANDY K2081</td><td>401</td></tr>
   <tr><td>ANDY K2080</td><td>501</td></tr>
   <tr><td>ANDY K2079</td><td>101</td></tr>...[LOOPS THROUGH ENTIRE FILE]
</table>

期望的输出:

<table>
    <tr><td>JUSTIN K2084</td><td>101</td></tr>
    <tr><td>ANDY K2083</td><td>201</td></tr>
    <tr><td>ANDY K2082</td><td>301</td></tr>
    <tr><td>ANDY K2081</td><td>401</td></tr>
    <tr><td>ANDY K2080</td><td>501</td></tr>
</table>

XSL:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="2.0">
    <xsl:output method="html" version="4.0" indent="yes"/>

    <xsl:template match="/">
        <table>
            <xsl:for-each select="SpeedOfService">
                <xsl:for-each-group select="ServiceTiming" group-by="TransactionNumber">
                    <xsl:sort select="position()" data-type="number" order="descending"/>
                    <xsl:for-each-group select="current-group()" group-by="VirtualDisplayId">                    
                        <xsl:if test="position()=1">
                            <tr><td><xsl:value-of select="current-group()[1]/TableName"/></td><td><xsl:value-of select="current-group()[1]/VirtualDisplayId"/></td></tr>
                        </xsl:if>
                    </xsl:for-each-group>
                </xsl:for-each-group>
            </xsl:for-each>
        </table>
    </xsl:template>
</xsl:stylesheet>

XML:

<SpeedOfService>
  <ServiceTiming>
    <TransactionNumber>7773317</TransactionNumber>
    <VirtualDisplayId>200</VirtualDisplayId>
    <TableName>ANDY K2078</TableName>
  </ServiceTiming>
  <ServiceTiming>
    <TransactionNumber>7773318</TransactionNumber>
    <VirtualDisplayId>100</VirtualDisplayId>
    <TableName>ANDY K2079</TableName>
  </ServiceTiming>
  <ServiceTiming>
    <TransactionNumber>7773319</TransactionNumber>
    <VirtualDisplayId>501</VirtualDisplayId>
    <TableName>ANDY K2080</TableName>
  </ServiceTiming>
  <ServiceTiming>
    <TransactionNumber>7773319</TransactionNumber>
    <VirtualDisplayId>401</VirtualDisplayId>
    <TableName>ANDY K2080</TableName>
  </ServiceTiming>
  <ServiceTiming>
    <TransactionNumber>7773319</TransactionNumber>
    <VirtualDisplayId>301</VirtualDisplayId>
    <TableName>ANDY K2080</TableName>
  </ServiceTiming>
  <ServiceTiming>
    <TransactionNumber>7773319</TransactionNumber>
    <VirtualDisplayId>201</VirtualDisplayId>
    <TableName>ANDY K2080</TableName>
  </ServiceTiming>
  <ServiceTiming>
    <TransactionNumber>7773319</TransactionNumber>
    <VirtualDisplayId>101</VirtualDisplayId>
    <TableName>ANDY K2080</TableName>
  </ServiceTiming>
  <ServiceTiming>
    <TransactionNumber>7773320</TransactionNumber>
    <VirtualDisplayId>401</VirtualDisplayId>
    <TableName>ANDY K2081</TableName>
  </ServiceTiming>
  <ServiceTiming>
    <TransactionNumber>7773320</TransactionNumber>
    <VirtualDisplayId>501</VirtualDisplayId>
    <TableName>ANDY K2081</TableName>
  </ServiceTiming>
  <ServiceTiming>
    <TransactionNumber>7773320</TransactionNumber>
    <VirtualDisplayId>301</VirtualDisplayId>
    <TableName>ANDY K2081</TableName>
  </ServiceTiming>
  <ServiceTiming>
    <TransactionNumber>7773320</TransactionNumber>
    <VirtualDisplayId>201</VirtualDisplayId>
    <TableName>ANDY K2081</TableName>
  </ServiceTiming>
  <ServiceTiming>
    <TransactionNumber>7773320</TransactionNumber>
    <VirtualDisplayId>101</VirtualDisplayId>
    <TableName>ANDY K2081</TableName>
  </ServiceTiming>
  <ServiceTiming>
    <TransactionNumber>7773321</TransactionNumber>
    <VirtualDisplayId>301</VirtualDisplayId>
    <TableName>ANDY K2082</TableName>
  </ServiceTiming>
  <ServiceTiming>
    <TransactionNumber>7773321</TransactionNumber>
    <VirtualDisplayId>501</VirtualDisplayId>
    <TableName>ANDY K2082</TableName>
  </ServiceTiming>
  <ServiceTiming>
    <TransactionNumber>7773321</TransactionNumber>
    <VirtualDisplayId>401</VirtualDisplayId>
    <TableName>ANDY K2082</TableName>
  </ServiceTiming>
  <ServiceTiming>
    <TransactionNumber>7773321</TransactionNumber>
    <VirtualDisplayId>201</VirtualDisplayId>
    <TableName>ANDY K2082</TableName>
  </ServiceTiming>
  <ServiceTiming>
    <TransactionNumber>7773321</TransactionNumber>
    <VirtualDisplayId>101</VirtualDisplayId>
    <TableName>ANDY K2082</TableName>
  </ServiceTiming>
  <ServiceTiming>
    <TransactionNumber>7773322</TransactionNumber>
    <VirtualDisplayId>201</VirtualDisplayId>
    <TableName>ANDY K2083</TableName>
  </ServiceTiming>
  <ServiceTiming>
    <TransactionNumber>7773322</TransactionNumber>
    <VirtualDisplayId>501</VirtualDisplayId>
    <TableName>ANDY K2083</TableName>
  </ServiceTiming>
  <ServiceTiming>
    <TransactionNumber>7773322</TransactionNumber>
    <VirtualDisplayId>401</VirtualDisplayId>
    <TableName>ANDY K2083</TableName>
  </ServiceTiming>
  <ServiceTiming>
    <TransactionNumber>7773322</TransactionNumber>
    <VirtualDisplayId>301</VirtualDisplayId>
    <TableName>ANDY K2083</TableName>
  </ServiceTiming>
  <ServiceTiming>
    <TransactionNumber>7773322</TransactionNumber>
    <VirtualDisplayId>101</VirtualDisplayId>
    <TableName>ANDY K2083</TableName>
  </ServiceTiming>
  <ServiceTiming>
    <TransactionNumber>7773323</TransactionNumber>
    <VirtualDisplayId>101</VirtualDisplayId>
    <TableName>ANDY K2084</TableName>
  </ServiceTiming>
  <ServiceTiming>
    <TransactionNumber>7773323</TransactionNumber>
    <VirtualDisplayId>501</VirtualDisplayId>
    <TableName>ANDY K2084</TableName>
  </ServiceTiming>
  <ServiceTiming>
    <TransactionNumber>7773323</TransactionNumber>
    <VirtualDisplayId>401</VirtualDisplayId>
    <TableName>ANDY K2084</TableName>
  </ServiceTiming>
  <ServiceTiming>
    <TransactionNumber>7773323</TransactionNumber>
    <VirtualDisplayId>301</VirtualDisplayId>
    <TableName>ANDY K2084</TableName>
  </ServiceTiming>
  <ServiceTiming>
    <TransactionNumber>7773323</TransactionNumber>
    <VirtualDisplayId>201</VirtualDisplayId>
    <TableName>ANDY K2084</TableName>
  </ServiceTiming>
    <ServiceTiming>
    <TransactionNumber>7773324</TransactionNumber>
    <VirtualDisplayId>101</VirtualDisplayId>
    <TableName>JUSTIN K2084</TableName>
  </ServiceTiming>
  <ServiceTiming>
    <TransactionNumber>7773324</TransactionNumber>
    <VirtualDisplayId>501</VirtualDisplayId>
    <TableName>JUSTIN K2084</TableName>
  </ServiceTiming>
  <ServiceTiming>
    <TransactionNumber>7773324</TransactionNumber>
    <VirtualDisplayId>401</VirtualDisplayId>
    <TableName>JUSTIN K2084</TableName>
  </ServiceTiming>
  <ServiceTiming>
    <TransactionNumber>7773324</TransactionNumber>
    <VirtualDisplayId>301</VirtualDisplayId>
    <TableName>JUSTIN K2084</TableName>
  </ServiceTiming>
  <ServiceTiming>
    <TransactionNumber>7773324</TransactionNumber>
    <VirtualDisplayId>201</VirtualDisplayId>
    <TableName>JUSTIN K2084</TableName>
  </ServiceTiming>
</SpeedOfService>

2 个答案:

答案 0 :(得分:0)

这是你需要的吗?

<xsl:template match="/table">
  <xsl:variable name="table" select="." />
  <xsl:for-each select="distinct-values(tr/td[2])">
    <xsl:variable name="group" select="."/>
    <xsl:copy-of select="$table/tr[td[2][text()=$group]][1]"/>
  </xsl:for-each>
</xsl:template>

小提琴:http://xsltransform.net/6pS1zCZ

答案 1 :(得分:0)

您当前的代码关闭,但其输出并不是您所需要的。让我们仔细看看它。

分解你的代码

for-each

这对性能或输出没有太大影响,但对您的情况来说似乎不合适。

<xsl:for-each select="SpeedOfService">

示例输入XML中只有一个SpeedOfService。你也没有对此做任何事情,只是循环遍历ServiceTiming个孩子。将XSL-y和更清晰的代码完全取出并将您的初始模板开头行更改为此代码将更多:

<xsl:template match="/SpeedOfService">

如果您的实际工作输入XML文件包含多个SpeedOfService元素,我建议您使用<xsl:apply-templates/>,然后创建单独的<xsl:template match="SpeedOfService">模板。

第一个for-each-group

<xsl:for-each-group select="ServiceTiming" group-by="TransactionNumber">
    <xsl:sort select="position()" data-type="number" order="descending"/>

这正是您需要它做的,并且正如您所描述的那样 - 它将所有ServiceTiming元素按TransactionNumber分组,从大到小。这会产生如下组:

<SAMPLE_GROUP>
    <ServiceTiming>
        <TransactionNumber>7773324</TransactionNumber>
        <VirtualDisplayId>101</VirtualDisplayId>
        <TableName>JUSTIN K2084</TableName>
    </ServiceTiming>
    <ServiceTiming>
        <TransactionNumber>7773324</TransactionNumber>
        <VirtualDisplayId>501</VirtualDisplayId>
        <TableName>JUSTIN K2084</TableName>
    </ServiceTiming>
    <ServiceTiming>
        <TransactionNumber>7773324</TransactionNumber>
        <VirtualDisplayId>401</VirtualDisplayId>
        <TableName>JUSTIN K2084</TableName>
    </ServiceTiming>
    <ServiceTiming>
        <TransactionNumber>7773324</TransactionNumber>
        <VirtualDisplayId>301</VirtualDisplayId>
        <TableName>JUSTIN K2084</TableName>
    </ServiceTiming>
    <ServiceTiming>
        <TransactionNumber>7773324</TransactionNumber>
        <VirtualDisplayId>201</VirtualDisplayId>
        <TableName>JUSTIN K2084</TableName>
    </ServiceTiming>
</SAMPLE_GROUP>

这是下一个稍微偏离轨道的地方。

第二个for-each-group

<xsl:for-each-group select="current-group()" group-by="VirtualDisplayId">

这需要current-group()并按VirtualDisplayId对这组元素进行分组。

这实际上就是应该做的。但是,由于数据集中的分组ServiceTiming元素集仅包含唯一VirtualDisplayId个值(所有JUSTIN K2084个值都有VirtualDisplayId个等),因此{ {1}}实际上并没有做任何有用的事情。

此外,在按for-each-group进行分组时,它仅将分组在我们刚刚制作的群组中 - 并且这些群组无法看到任何其他群组。因此,VirtualDisplayId群组的上下文包含JUSTIN K2084群组的任何上下文。因此,我们无法以任何方式对这些群组进行分组,从而对其他群组中的分组产生影响 - 这就是为什么您获得的输出具有两个不同的ANDY K2084值,但具有相同的{{1} }值。

TableName

VirtualDisplayId

这似乎是尝试将输出限制为该组中的第一组元素。而不是:

...
<tr>
    <td>JUSTIN K2084</td>
    <td>101</td>
</tr>
<tr>
    <td>ANDY K2084</td>
    <td>101</td>
</tr>
...

...我们可以使用if只是<xsl:for-each-group select="current-group()" group-by="VirtualDisplayId"> <xsl:if test="position()=1"> 相同的第一套,并完全保留select

[1]

...但我们已经确定xsl:if的分组无论如何都没有做任何事情。最后,在省略第二个<xsl:for-each-group select="current-group()[1]" group-by="VirtualDisplayId"> VirtualDisplayId之后,您的代码会输出相同的八个表行:

for-each-group

if语句

<xsl:for-each-group select="ServiceTiming" group-by="TransactionNumber"> <xsl:sort select="position()" data-type="number" order="descending"/> <tr><td><xsl:value-of select="current-group()[1]/TableName"/></td><td><xsl:value-of select="current-group()[1]/VirtualDisplayId"/></td></tr> </xsl:for-each-group> 的上下文中,选择“this”select与选择for-each-group是一回事。每the XSLT 2.0 specification(强调我的):

  

在序列构造函数中,上下文项是相关组的初始项 ...

我们可以因此收紧我们的代码:

.

输出:

current-group()[1]

将输出限制为前五个

我们可以通过添加检查<xsl:for-each-group select="ServiceTiming" group-by="TransactionNumber"> <xsl:sort select="position()" data-type="number" order="descending"/> <tr><td><xsl:value-of select="./TableName"/></td><td><xsl:value-of select="./VirtualDisplayId"/></td></tr> </xsl:for-each-group> 的{​​{1}}语句来限制输出:

  <tr>
     <td>JUSTIN K2084</td>
     <td>101</td>
  </tr>
  <tr>
     <td>ANDY K2084</td>
     <td>101</td>
  </tr>
  <tr>
     <td>ANDY K2083</td>
     <td>201</td>
  </tr>
  <tr>
     <td>ANDY K2082</td>
     <td>301</td>
  </tr>
  <tr>
     <td>ANDY K2081</td>
     <td>401</td>
  </tr>
  <tr>
     <td>ANDY K2080</td>
     <td>501</td>
  </tr>
  <tr>
     <td>ANDY K2079</td>
     <td>100</td>
  </tr>
  <tr>
     <td>ANDY K2078</td>
     <td>200</td>
  </tr>

虽然这确实只输出了五个表行,但这些值仍然不是我们想要的 - 我们仍然有两行,if值为position() < 6

另一种方法

那么我们如何先按<xsl:if test="position() &lt; 6"> <tr><td><xsl:value-of select="./TableName"/></td><td><xsl:value-of select="./VirtualDisplayId"/></td></tr> </xsl:if> 进行分组,然后按VirtualDisplayId进行第二次分组,但第二次分组是否知道第一个分组?

这样做的一种方法是将第一个分组的结果存储在一个变量中:我们在上面得到的八个表行。

101

然后我们将第二个分组应用于该变量。

TransactionNumber

我们使用VirtualDisplayId,因为<xsl:variable name="First_Group"> <xsl:for-each-group select="ServiceTiming" group-by="TransactionNumber"> <xsl:sort select="position()" data-type="number" order="descending"/> <tr><td><xsl:value-of select="./TableName"/></td><td><xsl:value-of select="./VirtualDisplayId"/></td></tr> </xsl:for-each-group> </xsl:variable> 值位于每行的第二个表格单元格中。

这让我们更接近我们想要的东西:我们现在有一组表格行,每<xsl:for-each-group select="$First_Group/tr" group-by="td[2]"> <xsl:copy-of select="."/> </xsl:for-each-group> 只出现一次。

group-by="td[2]"

要将表输出限制为前五行,我们现在添加VirtualDisplayId语句来检查位置:

VirtualDisplayId

我们得到了我们想要的输出:

<table>
    <tr>
        <td>JUSTIN K2084</td>
        <td>101</td>
    </tr>
    <tr>
        <td>ANDY K2083</td>
        <td>201</td>
    </tr>
    <tr>
        <td>ANDY K2082</td>
        <td>301</td>
    </tr>
    <tr>
        <td>ANDY K2081</td>
        <td>401</td>
    </tr>
    <tr>
        <td>ANDY K2080</td>
        <td>501</td>
    </tr>
    <tr>
        <td>ANDY K2079</td>
        <td>100</td>
    </tr>
    <tr>
        <td>ANDY K2078</td>
        <td>200</td>
    </tr>
</table

整个工作样式表

if