如何用XPath提取XML目标标签及其指定祖先标签(不含冗余子元素)
XPath解决方案:提取目标元素及其祖先层级(排除冗余子元素)
你要实现的需求核心是保留目标元素的祖先标签结构,但只保留目标元素作为祖先的唯一子元素,排除其他冗余子节点。下面分两种场景给出具体实现:
场景1:仅保留目标元素的直接父标签
假设你的目标元素是<c>,用XPath 2.0+可以直接构造符合要求的结构:
for $c in //c return element(local-name($c/parent::*), $c/parent::*/@*) { $c }
代码解释:
//c:定位XML中所有<c>元素for $c in //c:遍历每个找到的<c>element(local-name($c/parent::*), $c/parent::*/@*) { $c }:- 创建一个和父元素同名的新元素
- 继承父元素的所有属性(如果父元素有属性的话)
- 仅将当前
<c>作为该父元素的唯一子元素,自动排除父元素原来的其他子元素(比如示例中的<d>)
针对你给出的示例XML,执行后会输出:
<b><c/></b> <e><c/></e>
场景2:保留所有祖先标签(从根到父的完整层级)
如果需要保留从根节点到父节点的完整祖先链,同样用XPath 2.0+的递归构造实现:
for $c in //c let $ancestor-chain := reverse($c/ancestor::*) return fold-left($ancestor-chain, $c, function($inner-node, $ancestor) { element(local-name($ancestor), $ancestor/@*) { $inner-node } })
代码解释:
$ancestor-chain := reverse($c/ancestor::*):获取当前<c>的所有祖先节点,反转后得到从根到父的顺序fold-left(...):从最内层的<c>开始,依次将每个祖先元素包裹在外层,每个祖先元素仅包含内层的结构,自动排除其他冗余子元素
针对示例XML,执行后会输出:
<a><b><c/></b></a> <a><e><c/></e></a>
兼容XPath 1.0的方案(结合XSLT)
如果只能用XPath 1.0(无法直接构造新元素),可以配合XSLT模板实现需求:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- 入口:匹配根节点,处理所有<c>元素 --> <xsl:template match="/"> <xsl:apply-templates select="//c"/> </xsl:template> <!-- 处理<c>元素:调用wrap模式包裹父元素 --> <xsl:template match="c"> <xsl:apply-templates select="parent::*" mode="wrap"> <xsl:with-param name="child-node" select="."/> </xsl:template> </xsl:template> <!-- wrap模式:创建当前元素的副本,仅传入的子节点作为子元素 --> <xsl:template match="*" mode="wrap"> <xsl:element name="{local-name()}"> <xsl:copy-of select="@*"/> <xsl:param name="child-node"/> <xsl:copy-of select="$child-node"/> </xsl:element> </xsl:template> </xsl:stylesheet>
运行这个XSLT后,同样会输出你期望的<b><c/></b>和<e><c/></e>结构。
内容的提问来源于stack exchange,提问作者Alfe
相关产品推荐
相关产品推荐

