如何在XML文件中全局保留指定codes.head标签的首个实例
全局保留指定属性的首个XML标签实例的XSLT解决方案
需求说明
需要在整个XML文档中,仅保留codes.head标签里md.mnem属性值为ht1或ht1c的首个出现实例,所有后续符合条件的同属性标签都要剔除,完全不受父节点层级的限制。当前使用的XSLT只能在每个父块内保留对应实例,达不到全局筛选的要求。
示例XML
<root> <block1> <codes> <head md.mnem="ht1">First HT1</head> </codes> </block1> <block2> <codes> <head md.mnem="ht1c">First HT1C</head> </codes> <subblock> <codes> <head md.mnem="ht1">Second HT1 (需剔除)</head> <head md.mnem="ht1c">Second HT1C (需剔除)</head> </codes> </subblock> </block2> <block3> <codes> <head md.mnem="other">Other content</head> </codes> </block3> </root>
当前使用的XSLT脚本
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <xsl:template match="codes/head[@md.mnem='ht1' or @md.mnem='ht1c']"> <xsl:if test="not(preceding-sibling::codes/head[@md.mnem=current()/@md.mnem])"> <xsl:copy-of select="."/> </xsl:if> </xsl:template> </xsl:stylesheet>
当前输出结果
<root> <block1> <codes> <head md.mnem="ht1">First HT1</head> </codes> </block1> <block2> <codes> <head md.mnem="ht1c">First HT1C</head> </codes> <subblock> <codes> <head md.mnem="ht1">Second HT1 (需剔除)</head> <head md.mnem="ht1c">Second HT1C (需剔除)</head> </codes> </subblock> </block2> <block3> <codes> <head md.mnem="other">Other content</head> </codes> </block3> </root>
期望输出结果
<root> <block1> <codes> <head md.mnem="ht1">First HT1</head> </codes> </block1> <block2> <codes> <head md.mnem="ht1c">First HT1C</head> </codes> <subblock> <codes> <!-- 后续符合条件的实例已被剔除 --> </codes> </subblock> </block2> <block3> <codes> <head md.mnem="other">Other content</head> </codes> </block3> </root>
正确的XSLT实现方案
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- 全局索引符合条件的codes.head节点,按md.mnem属性分组 --> <xsl:key name="head-by-mnem" match="codes/head[@md.mnem='ht1' or @md.mnem='ht1c']" use="@md.mnem"/> <!-- 身份模板:复制所有未被特殊处理的节点和属性 --> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <!-- 处理符合条件的codes.head节点:仅输出每组的第一个实例 --> <xsl:template match="codes/head[@md.mnem='ht1' or @md.mnem='ht1c']"> <xsl:if test="generate-id() = generate-id(key('head-by-mnem', @md.mnem)[1])"> <xsl:copy-of select="."/> </xsl:if> </xsl:template> </xsl:stylesheet>
关键逻辑说明
- 全局索引:通过
<xsl:key>定义全局索引,把所有codes.head中md.mnem为ht1或ht1c的节点按属性值分组,确保跨层级访问所有符合条件的节点。 - 身份模板:保留原XML的所有结构,只对目标节点做特殊处理。
- 首个实例判断:用
generate-id()比较当前节点和对应分组的第一个节点的ID,只有ID相同(即该节点是全局第一个符合该属性值的实例)时才输出,其他符合条件的节点直接跳过。
内容的提问来源于stack exchange,提问作者Asha Choudhary
相关产品推荐
相关产品推荐

