You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在XSLT 1.0或2.0中按照ID与cont属性的特定规则统计重复案例

如何在XSLT 1.0或2.0中按照ID与cont属性的特定规则统计重复案例

我来帮你搞定这个需求!首先咱们先把核心的重复判定规则再梳理清楚,避免理解偏差:

  • 当cont属性为空时:每个<case>都是独立的实例,相同id的实例总数减1就是重复数(比如id=3有2个空cont案例,重复数就是1)
  • 当cont属性不为空时:一组<case>如果拥有相同的id和serial,并且它们的cont值构成了完整的分页序列(比如1 | 2和2 | 2是总页数为2的完整序列),这一组才算一个完整案例;相同id下这类完整案例的总数减1就是重复数(比如id=1有2组完整序列,重复数是1;id=4只有1组,重复数是0)

接下来分别给出XSLT 2.0和1.0的实现方案,都是针对你的需求定制的:


XSLT 2.0 实现方案

2.0版本支持更强大的分组和自定义函数,代码会更简洁易懂:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <!-- 辅助函数:从cont属性中提取总页数 -->
    <xsl:function name="local:get-total-pages" as="xs:integer">
        <xsl:param name="cont" as="xs:string"/>
        <!-- 按|分割字符串,取第二部分转成整数 -->
        <xsl:sequence select="xs:integer(tokenize($cont, '\s*\|\s*')[2])"/>
    </xsl:function>

    <xsl:template match="/cases">
        <output>
            <!-- 第一步:按id分组处理每个id下的所有案例 -->
            <xsl:for-each-group select="case" group-by="@id">
                <xsl:variable name="current-id" select="@id"/>
                <!-- 拆分当前id下的案例:空cont和非空cont两类 -->
                <xsl:variable name="empty-cont-cases" select="current-group()[@cont='']"/>
                <xsl:variable name="non-empty-cont-cases" select="current-group()[@cont!='']"/>

                <id val="{$current-id}">
                    <duplicates>
                        <xsl:choose>
                            <!-- 只有空cont的情况:直接用案例总数减1 -->
                            <xsl:when test="not($non-empty-cont-cases)">
                                <xsl:value-of select="count($empty-cont-cases) - 1"/>
                            </xsl:when>
                            <!-- 只有非空cont的情况:统计完整案例组数 -->
                            <xsl:when test="not($empty-cont-cases)">
                                <!-- 按serial分组,每个serial对应一个完整案例的分页 -->
                                <!-- 筛选出那些组内案例数量等于总页数的完整组 -->
                                <xsl:variable name="full-case-groups" select="
                                    for $group in group-by($non-empty-cont-cases, serial)
                                    return if (count($group) = local:get-total-pages($group[1]/@cont))
                                           then $group
                                           else ()
                                "/>
                                <!-- 重复数是完整案例组数减1 -->
                                <xsl:value-of select="count($full-case-groups) - 1"/>
                            </xsl:when>
                            <!-- 混合情况:同时有空和非空cont,这里默认相加两种情况的重复数,你可以根据实际需求调整 -->
                            <xsl:otherwise>
                                <xsl:variable name="empty-dupes" select="count($empty-cont-cases)-1"/>
                                <xsl:variable name="non-empty-dupes">
                                    <xsl:for-each select="group-by($non-empty-cont-cases, serial)">
                                        <xsl:if test="count(.) = local:get-total-pages(./@cont)">
                                            <xsl:text>1</xsl:text>
                                        </xsl:if>
                                    </xsl:for-each>
                                </xsl:variable>
                                <xsl:value-of select="$empty-dupes + (string-length($non-empty-dupes)-1)"/>
                            </xsl:otherwise>
                        </xsl:choose>
                    </duplicates>
                </id>
            </xsl:for-each-group>
        </output>
    </xsl:template>
</xsl:stylesheet>

代码说明

  1. 自定义函数local:get-total-pages专门用来从cont字符串里提取总页数,比如1 | 3会提取出3。
  2. 先按id把所有案例分组,再拆分出空cont和非空cont的案例分别处理。
  3. 对于非空cont的案例,先按serial分组(因为同一个完整案例的serial肯定相同),然后验证每组的案例数量是否等于该组的总页数,筛选出真正的完整案例组,最后统计组数计算重复数。

XSLT 1.0 实现方案

因为1.0不支持自定义函数和for-each-group,咱们用key和模板来模拟分组逻辑:

<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:strip-space elements="*"/>

    <!-- 按id分组的key -->
    <xsl:key name="case-by-id" match="case" use="@id"/>
    <!-- 按id+serial分组的key(专门处理非空cont的案例) -->
    <xsl:key name="case-by-id-serial" match="case[@cont!='']" use="concat(@id, '|', serial)"/>

    <!-- 辅助模板:提取cont中的总页数(1.0不支持函数,用模板替代) -->
    <xsl:template name="get-total-pages">
        <xsl:param name="cont"/>
        <xsl:variable name="after-pipe" select="substring-after($cont, '|')"/>
        <!-- 去掉空格转成数字 -->
        <xsl:value-of select="normalize-space($after-pipe)"/>
    </xsl:template>

    <xsl:template match="/cases">
        <output>
            <!-- 遍历每个唯一的id -->
            <xsl:for-each select="case[generate-id() = generate-id(key('case-by-id', @id)[1])]">
                <xsl:variable name="current-id" select="@id"/>
                <xsl:variable name="all-cases-for-id" select="key('case-by-id', $current-id)"/>
                <!-- 拆分空cont和非空cont案例 -->
                <xsl:variable name="empty-cont-cases" select="$all-cases-for-id[@cont='']"/>
                <xsl:variable name="non-empty-cont-cases" select="$all-cases-for-id[@cont!='']"/>

                <id val="{$current-id}">
                    <duplicates>
                        <xsl:choose>
                            <xsl:when test="not($non-empty-cont-cases)">
                                <!-- 只有空cont:总数减1 -->
                                <xsl:value-of select="count($empty-cont-cases) - 1"/>
                            </xsl:when>
                            <xsl:when test="not($empty-cont-cases)">
                                <!-- 非空cont:统计完整案例组数 -->
                                <xsl:variable name="full-case-count">
                                    <!-- 遍历每个唯一的id+serial组 -->
                                    <xsl:for-each select="$non-empty-cont-cases[generate-id() = generate-id(key('case-by-id-serial', concat(@id, '|', serial))[1])]">
                                        <xsl:variable name="current-group" select="key('case-by-id-serial', concat(@id, '|', serial))"/>
                                        <!-- 获取当前组的总页数 -->
                                        <xsl:variable name="total-pages">
                                            <xsl:call-template name="get-total-pages">
                                                <xsl:with-param name="cont" select="$current-group[1]/@cont"/>
                                            </xsl:call-template>
                                        </xsl:variable>
                                        <!-- 如果组内案例数量等于总页数,就记一个1 -->
                                        <xsl:if test="count($current-group) = number($total-pages)">
                                            <xsl:text>1</xsl:text>
                                        </xsl:if>
                                    </xsl:for-each>
                                </xsl:variable>
                                <!-- 字符串长度就是完整案例组数,减1得到重复数 -->
                                <xsl:value-of select="string-length($full-case-count) - 1"/>
                            </xsl:when>
                            <!-- 混合情况:默认相加两种重复数,可按需调整 -->
                            <xsl:otherwise>
                                <xsl:variable name="empty-dupes" select="count($empty-cont-cases)-1"/>
                                <xsl:variable name="non-empty-full-count">
                                    <xsl:for-each select="$non-empty-cont-cases[generate-id() = generate-id(key('case-by-id-serial', concat(@id, '|', serial))[1])]">
                                        <xsl:variable name="current-group" select="key('case-by-id-serial', concat(@id, '|', serial))"/>
                                        <xsl:variable name="total-pages">
                                            <xsl:call-template name="get-total-pages">
                                                <xsl:with-param name="cont" select="$current-group[1]/@cont"/>
                                            </xsl:call-template>
                                        </xsl:variable>
                                        <xsl:if test="count($current-group) = number($total-pages)">
                                            <xsl:text>1</xsl:text>
                                        </xsl:if>
                                    </xsl:for-each>
                                </xsl:variable>
                                <xsl:value-of select="$empty-dupes + (string-length($non-empty-full-count)-1)"/>
                            </xsl:otherwise>
                        </xsl:choose>
                    </duplicates>
                </id>
            </xsl:for-each>
        </output>
    </xsl:template>
</xsl:stylesheet>

代码说明

  1. 用两个key分别实现按id分组和按id+serial分组的逻辑。
  2. 用模板get-total-pages替代2.0的函数,提取cont中的总页数。
  3. 对于非空cont的案例,通过遍历唯一的id+serial组,验证每组是否是完整分页序列,然后用拼接字符串的方式统计符合条件的组数,最后计算重复数。

测试验证

把你提供的示例XML输入到这两个XSLT中,都会生成你预期的输出结果:

<?xml version="1.0" encoding="UTF-8"?>
<output>
   <id val="1">
      <duplicates>1</duplicates>
   </id>
   <id val="2">
      <duplicates>0</duplicates>
   </id>
   <id val="3">
      <duplicates>1</duplicates>
   </id>
   <id val="4">
      <duplicates>0</duplicates>
   </id>
</output>

如果你的场景中存在同一个id下既有空cont又有非空cont的混合情况,可以根据实际业务需求调整<xsl:otherwise>里的逻辑哦。

备注:内容来源于stack exchange,提问作者Somebody

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.22 07:08:08