基于XSLT 2.0/1.0实现XML元素遍历与文档访问逻辑的问询
XSLT实现方案:处理summary元素并加载关联文档的name元素
先把你的核心需求再明确下,确保没理解错:
- 遍历所有带
@name='summary'的元素 - 定位到该元素的父节点,收集父节点下所有后代
<info>元素(要排除当前这个@name='summary'的元素本身) - 对这些
<info>,根据@id对应的URI加载外部XML文档,输出文档里的<name>元素 - 特殊规则:如果当前
@name='summary'的元素带有@level='top',则跳过所有@duplicate='yes'的<info>元素
XSLT 2.0 实现
XSLT 2.0的语法更灵活,能写出更简洁的逻辑,代码如下:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes"/> <!-- 匹配所有@name='summary'的元素 --> <xsl:template match="*[@name='summary']"> <xsl:variable name="current-summary" select="."/> <!-- 获取父元素下的所有后代info,排除自身 --> <xsl:variable name="target-infos" select="$current-summary/ancestor::*[1]//info[not(. is $current-summary)]"/> <!-- 根据当前summary的level属性过滤info --> <xsl:variable name="filtered-infos" select=" if ($current-summary/@level='top') then $target-infos[not(@duplicate='yes')] else $target-infos "/> <!-- 遍历过滤后的info,加载对应文档并输出name --> <xsl:for-each select="$filtered-infos"> <xsl:variable name="doc-uri" select="@id"/> <!-- 加载外部文档,获取name元素 --> <xsl:copy-of select="document($doc-uri)//name"/> </xsl:for-each> </xsl:template> <!-- 匹配其他节点,避免默认输出原文档结构 --> <xsl:template match="text()|@*"/> </xsl:stylesheet>
代码说明:
- 先匹配所有带
@name='summary'的元素,用变量保存当前节点 - 通过
ancestor::*[1]定位到父元素,然后选取其下所有后代<info>,用not(. is $current-summary)排除自身 - 根据当前summary是否有
@level='top',决定是否过滤掉@duplicate='yes'的info元素 - 遍历过滤后的info,用
document($doc-uri)加载对应URI的XML文档,然后输出里面的所有<name>元素
XSLT 1.0 实现
XSLT 1.0没有is运算符,也不支持简洁的条件表达式,得用节点集比较和xsl:if来处理,同时文档加载要注意URI的处理:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common" extension-element-prefixes="exsl"> <xsl:output method="xml" indent="yes"/> <xsl:template match="*[@name='summary']"> <xsl:variable name="current-summary-id" select="generate-id(.)"/> <xsl:variable name="parent-node" select="parent::*"/> <!-- 收集父元素下的所有后代info,排除自身 --> <xsl:variable name="target-infos-raw"> <xsl:for-each select="$parent-node//info"> <xsl:if test="generate-id(.) != $current-summary-id"> <xsl:copy-of select="."/> </xsl:if> </xsl:for-each> </xsl:variable> <xsl:variable name="target-infos" select="exsl:node-set($target-infos-raw)/info"/> <!-- 过滤info:如果当前summary是top级别,跳过duplicate=yes的 --> <xsl:variable name="filtered-infos-raw"> <xsl:for-each select="$target-infos"> <xsl:if test="not($current-summary/@level='top' and @duplicate='yes')"> <xsl:copy-of select="."/> </xsl:if> </xsl:for-each> </xsl:variable> <xsl:variable name="filtered-infos" select="exsl:node-set($filtered-infos-raw)/info"/> <!-- 遍历加载文档并输出name --> <xsl:for-each select="$filtered-infos"> <xsl:variable name="doc-uri" select="@id"/> <xsl:copy-of select="document($doc-uri)//name"/> </xsl:for-each> </xsl:template> <xsl:template match="text()|@*"/> </xsl:stylesheet>
代码说明:
- 用
generate-id()来区分当前summary元素和其他info元素——因为XSLT 1.0没有is运算符,这是常用的替代方案 - 借助
exsl:node-set扩展函数(大部分XSLT 1.0处理器都支持)将临时生成的节点转换为可遍历的节点集 - 通过
xsl:if实现条件过滤:当当前summary是@level='top'时,跳过所有@duplicate='yes'的info - 同样用
document()加载外部文档,输出<name>元素
针对你给出的XML示例测试
比如你的示例结构:
<map> <info id="a.xml" level="top" name="summary"/> <map id="b.xml"> <info id="c.xml" name="summary"> <info id="d.xml" duplicate="yes"/> <info id="e.xml"/> </info> </map> </map>
- 对于
<info id="a.xml" level="top" name="summary"/>,会找其父元素<map>下的所有后代info,也就是<info id="d.xml" duplicate="yes"/>和<info id="e.xml"/>,但因为它是level='top',所以跳过duplicate='yes'的,只加载e.xml并输出其中的<name> - 对于
<info id="c.xml" name="summary">,会找其父元素<map id="b.xml">下的后代info,也就是自身下的两个info,排除自身后,两个都处理,加载d.xml和e.xml并输出它们的<name>
内容的提问来源于stack exchange,提问作者DitaNewbie La
相关产品推荐
相关产品推荐

