如何用XSLT 1.0提取每个<one>节点下不同父节点的唯一state值?
解决XSLT 1.0中合并两类节点下值并去重的问题
我懂你现在的困扰:之前分开用Muenchian分组处理<one>直接子节点的<Maddress>和<co>子节点下的<Maddress>,结果合并后出现了重复的<state>值,比如第一个<one>里的ak会被输出两次。这是因为你把两类节点的分组拆成了独立的key,没有把所有目标<state>放在统一的分组逻辑里,而且没限定分组的范围是当前<one>节点。
核心思路
要解决这个问题,我们需要:
- 把所有需要提取的
<state>节点(包括<one>直接下的<Maddress>和<co>下的<Maddress>)统一纳入一个key的匹配范围 - 分组时绑定到当前
<one>节点的上下文,确保每个<one>内部的<state>值独立去重(不会和其他<one>的state混淆)
完整XSLT实现代码
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- 定义key:将每个<one>节点的唯一标识与state值拼接作为分组键,确保每个one内部独立分组 --> <xsl:key name="state-by-one-and-value" match="one/Maddress/address/state | one/co/Maddress/address/state" use="concat(generate-id(ancestor::one), '|', .)" /> <!-- 根模板:触发对所有<one>节点的处理 --> <xsl:template match="/"> <xsl:apply-templates select="filename/root/one" /> </xsl:template> <!-- 处理每个<one>节点 --> <xsl:template match="one"> <!-- 生成序号,比如1.、2. --> <xsl:number format="1." /> <!-- 遍历当前one下所有唯一的state节点 --> <xsl:for-each select="(Maddress/address/state | co/Maddress/address/state)[generate-id() = generate-id(key('state-by-one-and-value', concat(generate-id(current()), '|', .))[1])]"> <!-- 除了第一个元素,前面加逗号分隔 --> <xsl:if test="position() > 1">, </xsl:if> <xsl:value-of select="." /> </xsl:for-each> <!-- 换行,让每个one的结果单独一行 --> <xsl:text> </xsl:text> </xsl:template> </xsl:stylesheet>
代码解释
Key的定义:
match属性同时匹配两类<state>节点:one/Maddress/address/state和one/co/Maddress/address/stateuse属性用concat(generate-id(ancestor::one), '|', .)把当前<one>节点的唯一ID和<state>的值拼接成分组键,这样每个<one>内部的相同<state>值会被分到同一组,不同<one>的相同值不会混淆。
节点的处理 :- 用
<xsl:number>生成你需要的序号格式 <xsl:for-each>筛选当前<one>下所有属于分组第一个节点的<state>,也就是去重后的唯一值- 用
<xsl:if>处理逗号分隔,避免第一个元素前出现多余的逗号
- 用
测试结果
用你提供的输入XML运行这个XSLT,会得到和你期望一致的输出:
1.ak, vr, fg 2.pp, fg, ak
为什么之前的代码会出错?
你之前的代码定义了两个独立的key,分别处理两类<Maddress>的<state>,然后在同一个<one>模板里分开遍历这两组去重后的结果。这样一来,比如第一个<one>里直接的<Maddress>有ak,<co>下的<Maddress>也有ak,就会被分别输出,导致重复。而我们的方案把所有目标<state>放在同一个分组逻辑里,自然就能在<one>内部完成全局去重。
内容的提问来源于stack exchange,提问作者Bhaskar Uday
相关产品推荐
相关产品推荐

