如何通过XPath查询XML匹配节点及其所有上级父节点(直至根节点)
获取XML目标节点及其所有非根父节点的属性值(XPath方案)
针对需求——定位目标节点后,输出该节点及所有非根父节点的@name属性,且无需提前固定层级结构,以下是几种可行方案:
1. XPath 2.0+ 原生递归表达式
如果你的工具支持XPath 2.0或更高版本(如Saxon、部分版本的xmlstarlet),可以直接用递归匹配表达式一次性获取所有目标属性:
//cup[@name='Shiny cup']/ancestor-or-self::*[not(local-name()='myStuff')]/@name
表达式说明:
ancestor-or-self::*:匹配当前节点(目标cup节点)及其所有祖先节点[not(local-name()='myStuff')]:过滤掉根节点myStuff/@name:提取符合条件节点的name属性
执行后会直接返回期望结果:
"Shiny cup", "Left Compartment", "Upper Shelf", "Yellow Cupboard", "Kitchen"
2. XPath 1.0 结合工具循环(兼容xmllint/xmlstarlet)
若只能用XPath 1.0(比如xmllint默认支持的版本),可以结合工具的多匹配模式或shell循环实现:
用xmlstarlet实现
xmlstarlet sel -t \ -m "//cup[@name='Shiny cup']" -v "@name" -n \ -m "//cup[@name='Shiny cup']/ancestor::*[not(local-name()='myStuff')]" -v "@name" -n \ input.xml
命令说明:
- 第一部分
-m "//cup[@name='Shiny cup']" -v "@name" -n:输出目标节点的name属性 - 第二部分
-m "//cup[@name='Shiny cup']/ancestor::*[not(local-name()='myStuff')]" -v "@name" -n:遍历所有非根父节点,依次输出它们的name属性
用xmllint结合shell循环实现
#!/bin/bash INPUT_FILE="input.xml" CURRENT_NODE="//cup[@name='Shiny cup']" # 循环遍历节点及父节点,直到遇到myStuff while true; do # 输出当前节点的name属性(处理xmlstarlet输出格式) xmllint --xpath "$CURRENT_NODE/@name" "$INPUT_FILE" | sed 's/ name="//;s/"//' echo # 切换到父节点 CURRENT_NODE="$CURRENT_NODE/.." # 获取父节点的标签名 PARENT_TAG=$(xmllint --xpath "name($CURRENT_NODE)" "$INPUT_FILE") # 遇到myStuff则终止循环 if [ "$PARENT_TAG" = "myStuff" ]; then break fi done
3. 编程语言中递归遍历(以Python lxml为例)
如果是在代码里处理,直接递归遍历父节点是最灵活的方式:
from lxml import etree # 加载XML文档 tree = etree.parse("input.xml") # 定位目标节点 target_node = tree.xpath("//cup[@name='Shiny cup']")[0] # 收集属性值 name_list = [] current = target_node while current.tag != "myStuff": name_list.append(current.get("name")) current = current.getparent() # 按目标格式输出 print('\n'.join(f'"{name}"' for name in name_list))
内容的提问来源于stack exchange,提问作者Kurtibert
相关产品推荐
相关产品推荐

