XML-FO转换中基于KeyName的XPath for-each去重循环需求
实现XML中唯一KeyName节点的单次XSL-FO转换
嘿,这个需求用XSLT的分组特性就能完美解决!我们可以先把重复的KeyName按文本值分组,然后只对每个分组里的第一个节点执行FO转换逻辑,这样就能保证每个唯一的KeyName只被处理一次。下面是完整的代码和说明:
第一步:定义分组键与基础FO结构
首先我们需要在XSLT里定义一个分组键,用来把相同KeyName的Row节点归为一组,同时搭建FO文档的基础框架:
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format"> <!-- 定义分组规则:按KeyName的文本值对Row节点分组 --> <xsl:key name="uniqueKeyGroups" match="Row" use="KeyName"/> <!-- 匹配根节点,生成FO文档的核心结构 --> <xsl:template match="/root"> <fo:root> <!-- 定义页面模板 --> <fo:layout-master-set> <fo:simple-page-master master-name="default-page" page-width="210mm" page-height="297mm"> <fo:region-body margin="20mm"/> </fo:simple-page-master> </fo:layout-master-set> <fo:page-sequence master-reference="default-page"> <fo:flow flow-name="xsl-region-body"> <!-- 只选择每个分组里的第一个Row节点,实现去重 --> <xsl:apply-templates select="Row[generate-id() = generate-id(key('uniqueKeyGroups', KeyName)[1])]"/> </fo:flow> </fo:page-sequence> </fo:root> </xsl:template>
第二步:编写唯一KeyName的FO处理逻辑
接下来,我们为每个唯一的KeyName编写具体的FO转换模板,这里以生成标题和示例内容为例,你可以根据自己的需求替换成对应的格式:
<!-- 处理每个唯一的KeyName分组 --> <xsl:template match="Row"> <!-- 输出KeyName作为加粗标题 --> <fo:block font-size="16pt" font-weight="bold" margin-bottom="8mm"> 唯一标识:<xsl:value-of select="KeyName"/> </fo:block> <!-- 这里添加你需要的自定义处理逻辑 --> <fo:block font-size="12pt" line-height="1.5" margin-bottom="12mm"> 针对<xsl:value-of select="KeyName"/>的专属FO内容,比如表格、段落、图片等都可以在这里定义。 </fo:block> </xsl:template> </xsl:stylesheet>
核心逻辑解释
<xsl:key>:这个标签定义了分组规则,把所有Row节点按照KeyName的文本值分成不同的组,相同KeyName的节点会被分到同一组。Row[generate-id() = generate-id(key('uniqueKeyGroups', KeyName)[1])]:这个选择器是关键!它会筛选出每个分组中的第一个Row节点,因为同一组内的所有节点调用generate-id(key('uniqueKeyGroups', KeyName)[1])都会得到同一个ID,只有组内第一个节点的generate-id()会和它相等,这样就实现了自动去重。- 你可以完全自定义
Row模板里的FO内容,比如生成报表表格、插入图片、设置复杂样式等,只要是该KeyName需要的处理逻辑都放在这里就行。
用这个XSLT处理你的XML后,重复的A和B都会只被处理一次,C也会正常输出,最终生成的FO文档里每个唯一KeyName对应一份处理结果。
内容的提问来源于stack exchange,提问作者Lars
相关产品推荐
相关产品推荐

