如何使用XSLT mode属性实现XML分组后仅输出文本结果
解决XSLT多模板组合时的输出问题
问题说明
尽管有不少帖子讲解同一XSLT文件中多模板的使用,但仍未找到解决自身问题的方案。希望对XML文件执行两次转换:首先按ID进行分组,然后进行去除特殊字符的处理。单独使用两个模板时均能正常工作,但将它们组合后,输出同时包含XML和文本内容。目标是仅保留文本输出,将XML部分作为中间步骤即可,期望实现类似如下结构的逻辑:
<xsl:template match=""> <xsl:apply-templates select="XML_Part"/> ---> 不输出到最终结果 <xsl:apply-templates select="Text_Part"/> </xsl:template> <xsl:template match="" mode="XML_Part"> </xsl:template> <xsl:template match="" mode="Text_Part"> </xsl:template>
输入XML文件
<?xml version="1.0" encoding="UTF-8"?> <wd:Report_Data xmlns:wd="http://www.workday.org/"> <wd:Report_Entry> <wd:id>1000</wd:id> <wd:name>Call-of-Duty</wd:name> <wd:price>50.12</wd:price> <wd:copy>0</wd:copy> </wd:Report_Entry> <wd:Report_Entry> <wd:id>1000</wd:id> <wd:name>Call-of-Duty</wd:name> <wd:price>60.0</wd:price> <wd:copy>1</wd:copy> </wd:Report_Entry> <wd:Report_Entry> <wd:id>2000</wd:id> <wd:name>Metal Slug</wd:name> <wd:price>75.00</wd:price> <wd:copy>0</wd:copy> </wd:Report_Entry> <wd:Report_Entry> <wd:id>3000</wd:id> <wd:name>Need For Speed</wd:name> <wd:price>40.00</wd:price> <wd:copy>0</wd:copy> </wd:Report_Entry> </wd:Report_Data>
分组用XSLT模板(单独正常工作)
<xsl:template match="/wd:Report_Data"> <xsl:copy> <xsl:for-each-group select="wd:Report_Entry" group-by="wd:id"> <xsl:copy> <xsl:copy-of select="wd:id"/> <xsl:copy-of select="wd:name"/> <wd:price> <xsl:value-of select="sum(current-group()/wd:price)" /> </wd:price> <xsl:copy-of select="wd:copy"/> </xsl:copy> </xsl:for-each-group> </xsl:copy> </xsl:template>
去除特殊字符并显示文本的XSLT模板(单独正常工作)
<xsl:template match="wd:Report_Data/wd:Report_Entry"> <xsl:value-of select="wd:id"/> <xsl:text>|</xsl:text> <xsl:value-of select="translate(wd:name,'-',' ')"/> <xsl:text>|</xsl:text> <xsl:value-of select="wd:price"/> <xsl:text>|</xsl:text> <xsl:value-of select="wd:copy"/> </xsl:template>
当前输出
<wd:Report_Data xmlns:wd="http://www.workday.org/"> <wd:Report_Entry> <wd:id>1000</wd:id> <wd:name>Call of Duty</wd:name> <wd:price>110.12</wd:price> <wd:copy>0</wd:copy> </wd:Report_Entry> <wd:Report_Entry> <wd:id>2000</wd:id> <wd:name>Metal Slug</wd:name> <wd:price>75</wd:price> <wd:copy>0</wd:copy> </wd:Report_Entry> <wd:Report_Entry> <wd:id>3000</wd:id> <wd:name>Need For Speed</wd:name> <wd:price>40</wd:price> <wd:copy>0</wd:copy> </wd:Report_Entry> </wd:Report_Data> 1000|Call of Duty|110.12|0 2000|Metal Slug|75.00|0 3000|Need For Speed|40.00|0
期望输出
1000|Call of Duty|110.12|0 2000|Metal Slug|75.00|0 3000|Need For Speed|40.00|0
解决方案
方案一:一步完成分组与文本转换
核心是直接在分组逻辑中调用文本输出模板,跳过中间XML的输出环节:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:wd="http://www.workday.org/"> <!-- 主模板:触发分组并直接生成文本 --> <xsl:template match="/wd:Report_Data"> <xsl:for-each-group select="wd:Report_Entry" group-by="wd:id"> <xsl:apply-templates select="." mode="text-output"/> <xsl:text> </xsl:text> <!-- 添加换行符 --> </xsl:for-each-group> </xsl:template> <!-- 文本输出模板:处理分组后的条目 --> <xsl:template match="wd:Report_Entry" mode="text-output"> <xsl:value-of select="wd:id"/> <xsl:text>|</xsl:text> <xsl:value-of select="translate(wd:name,'-',' ')"/> <xsl:text>|</xsl:text> <xsl:value-of select="sum(current-group()/wd:price)"/> <xsl:text>|</xsl:text> <xsl:value-of select="wd:copy"/> </xsl:template> <!-- 抑制默认模板,避免多余内容输出 --> <xsl:template match="text()|@*"/> </xsl:stylesheet>
方案二:保留中间XML步骤
如果严格需要先生成分组后的XML再转换为文本,可通过变量存储中间结果:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:wd="http://www.workday.org/"> <xsl:template match="/wd:Report_Data"> <!-- 第一步:生成分组后的XML并存储 --> <xsl:variable name="grouped-data"> <xsl:copy> <xsl:for-each-group select="wd:Report_Entry" group-by="wd:id"> <xsl:copy> <xsl:copy-of select="wd:id"/> <xsl:copy-of select="wd:name"/> <wd:price> <xsl:value-of select="sum(current-group()/wd:price)" /> </wd:price> <xsl:copy-of select="wd:copy"/> </xsl:copy> </xsl:for-each-group> </xsl:copy> </xsl:variable> <!-- 第二步:对中间XML应用文本输出模板 --> <xsl:apply-templates select="$grouped-data/wd:Report_Data/wd:Report_Entry"/> </xsl:template> <!-- 文本输出模板 --> <xsl:template match="wd:Report_Entry"> <xsl:value-of select="wd:id"/> <xsl:text>|</xsl:text> <xsl:value-of select="translate(wd:name,'-',' ')"/> <xsl:text>|</xsl:text> <xsl:value-of select="wd:price"/> <xsl:text>|</xsl:text> <xsl:value-of select="wd:copy"/> <xsl:text> </xsl:text> </xsl:template> <!-- 抑制默认模板 --> <xsl:template match="text()|@*"/> </xsl:stylesheet>
关键说明
mode属性用于区分不同处理逻辑,避免模板冲突;- 抑制默认模板(
match="text()|@*")可防止原始XML的多余内容被输出; - 添加
<xsl:text> </xsl:text>确保每条结果单独占一行,匹配期望格式。
内容的提问来源于stack exchange,提问作者David Edgar
相关产品推荐
相关产品推荐

