XSLT匹配:当值存在于列表/数组时提取指定XML节点
按指定ID列表提取XML中的节点
需求说明
有大量XML格式文档,需要根据一组ID值筛选<fig>节点:仅保留id属性值存在于目标ID列表中的<fig>节点,其余忽略。尝试过使用参数、变量及exsl:node-set但未成功。
输入XML
<?xml version="1.0" encoding="UTF-8"?> <sec xmlns:xlink="http://www.w3.org/1999/xlink"> <title>Section Title</title> <fig id="img01"> <caption><title>Image 1</title></caption> <graphic xlink:href="img01.eps"/> </fig> <fig id="img02"> <caption><title>Image 2</title></caption> <graphic xlink:href="img02.eps"/> </fig> </sec>
目标ID列表
- img02
- img12
- img23
- img34
- (更多ID)
期望输出XML
<?xml version="1.0" encoding="UTF-8"?> <sec xmlns:xlink="http://www.w3.org/1999/xlink"> <title>Section Title</title> <fig id="img02"> <caption><title>Image 2</title></caption> <graphic xlink:href="img02.eps"/> </fig> </sec>
尝试的XSLT代码
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common" xmlns:xlink="http://www.w3.org/1999/xlink" exclude-result-prefixes="exsl" version="2"> <xsl:param name="ListOfImages" as="element()"> <item>img02</item> <item>hundreds-of-image-IDs</item> </xsl:param> <xsl:template match="/"> <sec> <xsl:apply-templates/> </sec> </xsl:template> <xsl:template match="fig[exsl:node-set($ListOfImages)/item = exsl:node-set(@id)]"> <p>Image Name: <xsl:value-of select="@id"/></p> <xsl:copy-of select="."/> </xsl:template> <xsl:template match="@* | node()"> <xsl:apply-templates/> </xsl:template> </xsl:stylesheet>
问题分析与解决方案
你的代码存在几个关键问题:
- XSLT版本误用:已声明使用XSLT 2.0,却仍用了仅XSLT 1.0需要的
exsl:node-set(),XSLT 2.0支持直接处理节点集参数,无需转换。 - 匹配条件错误:
exsl:node-set(@id)是错误用法,@id是属性节点,不需要用node-set转换,直接比较即可。 - 模板逻辑问题:默认模板会忽略元素的属性和结构,导致输出的
<sec>丢失命名空间,且不需要额外输出<p>标签。
正确的XSLT 2.0实现
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xlink="http://www.w3.org/1999/xlink" version="2.0"> <!-- 定义目标ID列表参数,用元素节点存储 --> <xsl:param name="ListOfImages"> <item>img02</item> <item>img12</item> <item>img23</item> <item>img34</item> </xsl:param> <!-- 复制整个文档结构,仅筛选指定fig节点 --> <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> <!-- 匹配id不在目标列表中的fig节点,不输出 --> <xsl:template match="fig[not(@id = $ListOfImages/item)]"/> </xsl:stylesheet>
代码说明
- 身份模板:
match="@* | node()"的模板会复制所有原节点和属性,保证文档结构、命名空间等信息完整保留。 - 筛选逻辑:专门匹配
id不在目标列表中的<fig>节点,且模板为空,相当于删除这些节点;符合条件的<fig>会被身份模板正常复制。 - 参数简化:XSLT 2.0中直接使用
$ListOfImages/item即可获取所有目标ID值,无需额外转换。
若需兼容XSLT 1.0
如果你的处理器仅支持XSLT 1.0,可调整参数为字符串列表,用contains结合分隔符判断:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.0"> <!-- 用逗号分隔的字符串作为ID列表参数 --> <xsl:param name="ListOfImages" select="'img02,img12,img23,img34'"/> <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> <!-- 匹配id不在目标列表中的fig节点 --> <xsl:template match="fig[not(contains(concat(',', $ListOfImages, ','), concat(',', @id, ',')))]"/> </xsl:stylesheet>
内容的提问来源于stack exchange,提问作者LadyCygnus
相关产品推荐
相关产品推荐

