基于ALF、OND分组K18并按指定顺序排列子段的XSLT问题
解决方案
1. XSLT 1.0 兼容的Muenchian分组实现
先定义分组键,用ALF和OND的组合值来分组K18段:
<xsl:key name="k18Group" match="K18" use="concat(ALF, '|', OND)" />
然后处理每个分组的首个K18节点,把同组的所有子段按指定顺序整合到新的K18里:
<!-- 遍历每个K18分组的第一个节点 --> <xsl:for-each select="K18[generate-id() = generate-id(key('k18Group', concat(ALF, '|', OND))[1])]"> <K18> <!-- 保留当前分组的ALF和OND字段 --> <xsl:copy-of select="ALF | OND" /> <!-- 严格按 P04Y → P04Z → P04 → P03 的顺序输出子段 --> <xsl:copy-of select="key('k18Group', concat(ALF, '|', OND))/P04Y" /> <xsl:copy-of select="key('k18Group', concat(ALF, '|', OND))/P04Z" /> <xsl:copy-of select="key('k18Group', concat(ALF, '|', OND))/P04" /> <xsl:copy-of select="key('k18Group', concat(ALF, '|', OND))/P03" /> </K18> </xsl:for-each>
这种方法不用依赖sort函数,直接按你要的顺序挨个提取对应子段就行——要是某个子段不存在,copy-of不会输出任何内容,自动忽略掉了。
2. XSLT 2.0+ 简化分组实现
如果用的是XSLT 2.0及以上版本,用xsl:for-each-group会更简洁:
<xsl:for-each-group select="K18" group-by="concat(ALF, '|', OND)"> <K18> <!-- 取分组内第一个节点的ALF和OND(同组值一致) --> <xsl:copy-of select="current-group()[1]/ALF | current-group()[1]/OND" /> <!-- 按指定顺序输出子段 --> <xsl:copy-of select="current-group()/P04Y" /> <xsl:copy-of select="current-group()/P04Z" /> <xsl:copy-of select="current-group()/P04" /> <xsl:copy-of select="current-group()/P03" /> </K18> </xsl:for-each-group>
3. 解决分组内容被放到最后的问题
你之前的XSLT把分组内容放最后,大概率是因为在处理完所有其他段之后才输出分组后的K18。要避免这个问题,可以这么做:
- 先按原顺序输出所有非K18的段
- 再输出分组后的K18;或者如果要保留K18在原上下文的位置,就遍历所有节点时,遇到分组的第一个K18就输出整合后的内容,跳过同组其他K18。
示例(保留非K18段的原顺序,替换所有K18为分组结果):
<xsl:template match="/*"> <xsl:copy> <!-- 先输出所有非K18的节点,保持原顺序 --> <xsl:apply-templates select="node()[not(self::K18)]" /> <!-- 输出分组后的K18段 --> <xsl:for-each select="K18[generate-id() = generate-id(key('k18Group', concat(ALF, '|', OND))[1])]"> <K18> <xsl:copy-of select="ALF | OND" /> <xsl:copy-of select="key('k18Group', concat(ALF, '|', OND))/P04Y" /> <xsl:copy-of select="key('k18Group', concat(ALF, '|', OND))/P04Z" /> <xsl:copy-of select="key('k18Group', concat(ALF, '|', OND))/P04" /> <xsl:copy-of select="key('k18Group', concat(ALF, '|', OND))/P03" /> </K18> </xsl:for-each> </xsl:copy> </xsl:template> <!-- 空模板跳过原K18节点,防止重复输出 --> <xsl:template match="K18" />
关键提醒
别用普通的xsl:sort按节点名称排序,因为字母顺序是P03 < P04 < P04Y < P04Z,刚好和你要的顺序相反。直接按指定顺序提取子段才是最稳妥的方式,而且天然支持可选段——没有的段会自动被忽略,不用额外处理。
内容的提问来源于stack exchange,提问作者CHAR
相关产品推荐
相关产品推荐

