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

如何用XSLT实现XML按车型日期分组生成指定格式文本文件

解决XML按车型+日期分组生成指定格式文本问题

需求:将指定XML转换为文本文件,要求车型(Cars)和日期(Date)仅在每组头部出现一次,下方列出该组对应的时间(Time)与平均值(avg),按日期拆分头部,此前尝试分组方式未得到预期结果,现提供可行实现方案。

输入XML

<?xml version="1.0" encoding="UTF-8"?>
<data>
    <item>
        <Date>2023-02-19</Date>
        <Cars>Ford</Cars>
        <SellTarget>A4+</SellTarget>
        <Time>10:40:09</Time>
        <avg>19.3464027998</avg>
    </item>
    <item>
        <Date>2023-02-19</Date>
        <Cars>Ford</Cars>
        <SellTarget>A4+</SellTarget>
        <Time>11:21:56</Time>
        <avg>32.7150023474</avg>
    </item>
    <item>
        <Date>2023-02-19</Date>
        <Cars>Ford</Cars>
        <SellTarget>A4+</SellTarget>
        <Time>19:01:27</Time>
        <avg>554.0289810087</avg>
    </item>
    <item>
        <Date>2023-02-19</Date>
        <Cars>Ford</Cars>
        <SellTarget>A4+</SellTarget>
        <Time>23:15:26</Time>
        <avg>46.1398232343</avg>
    </item>
    <item>
        <Date>2023-02-19</Date>
        <Cars>Opel</Cars>
        <SellTarget>A4+</SellTarget>
        <Time>14:05:51</Time>
        <avg>41.7428144493</avg>
    </item>
    <item>
        <Date>2023-02-19</Date>
        <Cars>Opel</Cars>
        <SellTarget>A4+</SellTarget>
        <Time>15:01:02</Time>
        <avg>65.6303001034</avg>
    </item>
    <item>
        <Date>2023-02-19</Date>
        <Cars>Opel</Cars>
        <SellTarget>A4+</SellTarget>
        <Time>02:00:00</Time>
        <avg>1.2954721559</avg>
    </item>
</data>

当前使用的XSLT

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="text" encoding="UTF-8"/>

    <xsl:key name="carsgroup" match="/data/item" use="concat(Cars, '|', Date)" />

    <xsl:variable name="tab" select="'&#09;'"/>
    <xsl:variable name="newLine" select="'&#10;'"/>
    <xsl:variable name="csvSeparator" select="$tab"/>
    
    <xsl:template match="/">
        <xsl:call-template name="headerlines"/>
        <xsl:apply-templates select="data/item[generate-id() = generate-id(key('carsgroup', 
            concat(Cars, '|', Date)))]" mode="dataRows">
        </xsl:apply-templates>
    </xsl:template>
    
    <xsl:template name="headerlines">
        <!-- 1. line is Skipped -->
        <xsl:call-template name="csv">
            <xsl:with-param name="val" select="''"/>
        </xsl:call-template>
        <!-- 2. line is Skipped-->
        <xsl:call-template name="csv">
            <xsl:with-param name="val" select="''"/>
        </xsl:call-template>
        <!-- 3. line is Skipped -->
        <xsl:call-template name="csv">
            <xsl:with-param name="val" select="''"/>
        </xsl:call-template>
        <!-- 4. line is Skipped -->
        <xsl:call-template name="csv">
            <xsl:with-param name="val" select="''"/>
            <xsl:with-param name="isLastCol" select="true()"/>
        </xsl:call-template>
    </xsl:template>

    <xsl:template match="item" mode="dataRows">
        <!-- at 5. line the first 5 char is skipped -->
        <xsl:call-template name="csv">
            <xsl:with-param name="val" select="concat('    ', Cars, ' / ', Date, ' [ 50 Entrants ]')"/>
        </xsl:call-template>
        <!-- sequence number -->
        <xsl:call-template name="csv">
            <xsl:with-param name="val" select="position()"/>
        </xsl:call-template>
        <!-- Time HH:MM:SS -->
        <xsl:call-template name="csv">
            <xsl:with-param name="val" select="Time"/>
        </xsl:call-template>
        <!-- avg -->
        <xsl:call-template name="csv">
            <xsl:with-param name="val" select="avg"/>
            <xsl:with-param name="isLastCol" select="true()"/>
            <xsl:with-param name="isEOF" select="position()=last()"/>
        </xsl:call-template>
    </xsl:template>

    <xsl:template name="csv">
        <xsl:param name="val"/>
        <xsl:param name="isLastCol" select="false()"/>
        <xsl:param name="isEOF" select="false()"/>
        <xsl:value-of select="$val"/>
        <xsl:choose>
            <xsl:when test="$isLastCol and not($isEOF)">
                <xsl:value-of select="$newLine"/>
            </xsl:when>
            <xsl:when test="not($isEOF)">
                <xsl:value-of select="$csvSeparator"/>
            </xsl:when>
        </xsl:choose>
    </xsl:template>

</xsl:stylesheet>

当前生成的输出

Ford / 2023-02-19 [ 50 Entrants ]  1   10:40:09    19.3464027998
 Opel / 2023-02-19 [ 50 Entrants ]  2   14:05:51    41.7428144493

期望的输出

Ford / 2023-02-19 [ 50 Entrants ]
1   10:40:09    19.3464027998
2   11:21:56    32.7150023474
3   19:01:27    554.0289810087
4   23:15:26    46.1398232343
     Opel / 2023-02-19 [ 50 Entrants ]
1   02:00:00    1.2954721559
2   14:05:51    41.7428144493
3   15:01:02    65.6303001034

修改后的XSLT实现

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="text" encoding="UTF-8"/>

    <xsl:key name="carsgroup" match="/data/item" use="concat(Cars, '|', Date)" />

    <xsl:variable name="tab" select="'&#09;'"/>
    <xsl:variable name="newLine" select="'&#10;'"/>
    
    <xsl:template match="/">
        <!-- 遍历每个分组的第一个item,作为组的入口 -->
        <xsl:apply-templates select="data/item[generate-id() = generate-id(key('carsgroup', concat(Cars, '|', Date)))]" />
    </xsl:template>

    <xsl:template match="item">
        <!-- 输出组头,注意缩进和格式 -->
        <xsl:value-of select="concat('     ', Cars, ' / ', Date, ' [ 50 Entrants ]', $newLine)"/>
        <!-- 遍历当前组下的所有item -->
        <xsl:for-each select="key('carsgroup', concat(Cars, '|', Date))">
            <!-- 按时间排序(匹配期望输出中Opel组的时间顺序) -->
            <xsl:sort select="Time" data-type="text"/>
            <!-- 输出序号、时间、平均值,用制表符分隔 -->
            <xsl:value-of select="concat(position(), $tab, Time, $tab, avg, $newLine)"/>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

关键修改说明

  1. 移除多余空行:删除原headerlines模板,直接从组头开始输出,匹配期望格式
  2. 分组内遍历所有项:在分组模板中,通过key('carsgroup', concat(Cars, '|', Date))获取当前组的所有item,而非仅处理分组的第一个item
  3. 组内独立序号:使用xsl:for-each的position()生成每组内部的序号,而非全局序号
  4. 时间排序:添加<xsl:sort select="Time" data-type="text"/>,确保同组内按时间顺序输出,匹配期望的Opel组时间排序
  5. 简化格式输出:直接拼接文本和分隔符,避免复杂的csv模板,更易维护

内容的提问来源于stack exchange,提问作者gyuszmok

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 09:29:55