【问题标题】:Need XSLT transform to count elements returned from applied template需要 XSLT 转换来计算从应用模板返回的元素
【发布时间】:2021-09-13 15:03:29
【问题描述】:

我有一个非常相似的问题:Need XSLT transform to remove duplicate elements - sorted by an attribute。我从 最佳答案 中获取了解决方案并稍作修改。我必须计算状态,但只考虑最新结果。

我的模板如下所示:

<xsl:template match='Results/Result' mode='countstatus'>
    <xsl:param name='status' select='"Pass"'/>
    <xsl:for-each select="key('sn-key', SerialNumber)">
        <xsl:sort select='./Date' order='descending'/>
        <xsl:if test='((position() = 1) and (./Status=$status))'>
            <xsl:copy-of select="."/>
        </xsl:if>
    </xsl:for-each>
</xsl:template>

我不想显示所有匹配的结果,而只想显示应用此模板后返回的项目数。

我的键定义是&lt;xsl:key name='sn-key' match='Results/Result' use='SerialNumber'/&gt;,我将此模板称为&lt;xsl:apply-templates select='Results/Result[generate-id() = generate-id(key("sn-key", SerialNumber)[1])]' mode='countstatus'/&gt;

编辑

头脑清醒,我发现我的问题有点不清楚。这里有更多细节。

我的输入如下所示:

<Results>
    <Result ID="0">
        <SerialNumber>3333</SerialNumber>
        <Status>Fail</Status>
        <Date>21</Date>
    </Result>
    <Result ID="1">
        <SerialNumber>1111</SerialNumber>
        <Status>Fail</Status>
        <Date>34</Date>
    </Result>
    <Result ID="2">
        <SerialNumber>1111</SerialNumber>
        <Status>Pass</Status>
        <Date>67</Date>
    </Result>
    <Result ID="3">
        <SerialNumber>2222</SerialNumber>
        <Status>Fail</Status>
        <Date>40</Date>
    </Result>
    <Result ID="4">
        <SerialNumber>1111</SerialNumber>
        <Status>Fail</Status>
        <Date>55</Date>
    </Result>
    <Result ID="5">
        <SerialNumber>1111</SerialNumber>
        <Status>Fail</Status>
        <Date>88</Date>
    </Result>
    <Result ID="6">
        <SerialNumber>2222</SerialNumber>
        <Status>Fail</Status>
        <Date>22</Date>
    </Result>
    <Result ID="7">
        <SerialNumber>1111</SerialNumber>
        <Status>Fail</Status>
        <Date>86</Date>
    </Result>
    <Result ID="8">
        <SerialNumber>3333</SerialNumber>
        <Status>Pass</Status>
        <Date>99</Date>
    </Result>
</Results>

我想数一下,例如Fail 状态出现多少次,但只考虑 SerialNumber 的最新结果。我之前展示的模板显示了SerialNumber 的所有最新结果(但状态为Pass)。我想只显示数字。例如,在查找失败次数时,我想显示 2 而不是 [1111 Fail 88, 2222 Fail 40]

我的转变现在看起来像这样(但我需要别的东西):

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

<xsl:key name='sn-key' match='Results/Result' use='SerialNumber' />

<xsl:template match="/">
    <xsl:apply-templates select="Results/Result[generate-id()=generate-id(key('sn-key', SerialNumber)[1])]"/>
</xsl:template>

<xsl:template match="Results/Result">
    <xsl:param name='status' select='"Fail"'/>
    <xsl:for-each select="key('sn-key', SerialNumber)">
        <xsl:sort select="./Date" order="descending"/>
        <xsl:if test='((position() = 1) and (./Status=$status))'>
            <xsl:value-of select="."/><br />
        </xsl:if>
    </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

【问题讨论】:

    标签: xml xslt counting muenchian-grouping


    【解决方案1】:

    --- 为回应澄清而编辑---

    我想数一下,例如Fail 状态出现多少次,但只考虑 SerialNumber 的最新结果。

    我相信这可以转化为:

    • Results 按序列号分组;
    • 统计最近ResultStatus 为“失败”的组

    具体方法取决于Results 是否在输入 XML 中按时间顺序列出。

    如果答案是肯定的,您可以简单地修改 Muenchian 方法,在每个组中选择 last(而不是通常的firstResult,添加另一个谓词只保留那些Status 是“失败”的人并计算这个集合。这是一个单行:

    XSLT 1.0

    <xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    
    <xsl:key name="result-by-sn" match="Result" use="SerialNumber" />
    
    <xsl:template match="/Results">
        <output>
            <xsl:value-of select="count(Result[count(. | key('result-by-sn', SerialNumber)[last()]) = 1][Status='Fail'])"/>
        </output>
    </xsl:template>
    
    </xsl:stylesheet>
    

    如果答案是否定的,那么你别无选择,只能实际进行分组、排序和过滤成一个变量,然后计算结果:

    XSLT 1.0

    <xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    
    <xsl:key name="result-by-sn" match="Result" use="SerialNumber" />
    
    <xsl:template match="/Results">
        <xsl:variable name="temp">
            <xsl:for-each select="Result[count(. | key('result-by-sn', SerialNumber)[1]) = 1]">
                <xsl:for-each select="key('result-by-sn', SerialNumber)">
                    <xsl:sort select="Date" order="descending"/>
                    <xsl:if test="position()=1 and Status='Fail'">x</xsl:if>
                </xsl:for-each>
            </xsl:for-each>
        </xsl:variable>
        <output>
            <xsl:value-of select="string-length($temp)"/>
        </output>
    </xsl:template>
    
    </xsl:stylesheet>
    

    【讨论】:

    • 如果组没有排序怎么办(在我的情况下,我需要先按日期排序)?你能解释一下count(. | key('record_by_category', category)的输入参数是什么吗?
    • 我已根据您的说明修改了我的帖子。希望这能回答您可能遇到的任何问题。
    • 是否可以将排序结果存储在全局变量中,然后使用第一种方法?我尝试使用 exslt:node-set() 函数,但看起来我遇到了一些上下文问题。
    • 这是可能的,但由于节点集与 RTF 的问题,它会更复杂。如果您想这样做并且无法解决,我建议您发布一个单独的问题。
    • 那是 Muenchian 分组。我正在使用您的Result[generate-id() = generate-id(key("sn-key", SerialNumber)[1])] 的替代表达式。你会在原文中找到解释:jenitennison.com/xslt/grouping/muenchian.html
    猜你喜欢
    • 2013-01-26
    • 2021-10-05
    • 2014-10-09
    • 1970-01-01
    • 2019-02-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-19
    相关资源
    最近更新 更多