如何用xsl:for-each为每个匹配实例生成按规则命名的独立输出文件?
实现按实例生成独立XML/HTML文件的XSLT方案
需求说明
我有三个文件:1b.xml、2b.xml和try.xsl,想要实现以下目标:
- 通过
xsl:for-each遍历每个<bb>实例,为每个实例生成单独的XML文件(格式如示例) - 生成的文件名以
<bb>的@date和@num(对应<dd>的@id)命名 - 利用
<bb>的@class值,匹配<gg>和<vv>中对应class值的内容
期望输出格式
<meny> <mains> <meat>chicken</meat> <fish>trout</fish> <vegetarian>moussaka</vegetarian> </mains> <extra> <cake>Napoleon cake</cake> <drinks>white</drinks> </extra> </meny>
现有文件内容
1. try.xsl(当前代码)
<xsl:template match="aa"> <xsl:variable name="doc2" select="document('2b.xml')"/> <body> <table> <xsl:for-each select="bb"> <xsl:variable name="bb-num" select="@num"/> <xsl:variable name="dd" select="$doc2/cc/dd[@id=$bb-num]"/> <xsl:variable name="bb-class" select="@class"/> <xsl:variable name="gg" select="$dd/ff/gg[@class=$bb-class]"/> <xsl:variable name="vv" select="$dd/ff/vv[@class=$bb-class]"/> <tr> <td><xsl:value-of select="@date"/></td> <td><xsl:value-of select="$dd/mm/ee"/></td> <td><xsl:value-of select="$gg"/></td> <td><xsl:value-of select="$vv"/></td> </tr> </xsl:for-each> </table> </body> </xsl:template>
2. 1b.xml
<aa> <bb date="31.12.2022" num="prs1" class="alt_2"></bb> <bb date="07.01.2023" num="prs2" class="alt_1"></bb> </aa>
3. 2b.xml
<cc> <dd id="prs1"> <mm> <ee>steak</ee> <nn>salmon</nn> <oo>falaffel</oo> </mm> <ff> <gg class="alt_1">carrot cake</gg> <gg class="alt_2">chocolate cake</gg> <gg class="alt_3">cream cake</gg> <vv class="alt_1">red</vv> <vv class="alt_2">white</vv> <vv class="alt_3">sparkle</vv> </ff> </dd> <dd id="prs2"> <mm> <ee>chicken</ee> <nn>trout</nn> <oo>moussaka</oo> </mm> <ff> <gg class="alt_1">Swiss roll</gg> <gg class="alt_2">Napoleon cake</gg> <gg class="alt_3">marzipan cake</gg> <vv class="alt_1">red</vv> <vv class="alt_2">white</vv> <vv class="alt_3">sparkle</vv> </ff> </dd> </cc>
问题
请问是否可以实现上述需求?
可以实现,以下是修改后的XSLT方案
要生成多个独立文件,需要使用XSLT 2.0及以上版本的<xsl:result-document>元素,它支持动态创建输出文件。以下是适配需求的完整XSLT代码:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes" encoding="UTF-8"/> <xsl:template match="/aa"> <xsl:variable name="doc2" select="document('2b.xml')"/> <!-- 遍历每个bb节点,生成单独文件 --> <xsl:for-each select="bb"> <xsl:variable name="bb-date" select="@date"/> <xsl:variable name="bb-num" select="@num"/> <!-- 构造文件名:替换日期中的点为下划线,避免系统路径问题 --> <xsl:variable name="filename" select="concat(replace($bb-date, '\.', '_'), '_', $bb-num, '.xml')"/> <xsl:variable name="dd" select="$doc2/cc/dd[@id=$bb-num]"/> <xsl:variable name="bb-class" select="@class"/> <xsl:variable name="target-gg" select="$dd/ff/gg[@class=$bb-class]"/> <xsl:variable name="target-vv" select="$dd/ff/vv[@class=$bb-class]"/> <!-- 生成独立输出文件 --> <xsl:result-document href="{$filename}"> <meny> <mains> <meat><xsl:value-of select="$dd/mm/ee"/></meat> <fish><xsl:value-of select="$dd/mm/nn"/></fish> <vegetarian><xsl:value-of select="$dd/mm/oo"/></vegetarian> </mains> <extra> <cake><xsl:value-of select="$target-gg"/></cake> <drinks><xsl:value-of select="$target-vv"/></drinks> </extra> </meny> </xsl:result-document> </xsl:for-each> </xsl:template> </xsl:stylesheet>
关键说明
- 多文件生成:通过
<xsl:result-document>指定每个文件的路径和名称,这里用concat和replace处理日期格式,将31.12.2022转为31_12_2022,避免文件名中的点引发系统识别问题。 - 数据匹配:保留了原代码中通过
@num关联<bb>和<dd>,再通过@class匹配<gg>、<vv>的逻辑,确保数据对应正确。 - 输出结构:严格按照给出的期望格式构造
<meny>、<mains>、<extra>节点,映射对应的数据字段。
执行要求
需要使用支持XSLT 2.0的处理器(如Saxon 9+、Altova XMLSpy等),XSLT 1.0原生不支持多文件输出,无法实现此需求。
内容的提问来源于stack exchange,提问作者Mats Køber
相关产品推荐
相关产品推荐

