xml - 平面结构中的 XPath 递归“父”选择
问题描述
给出了以下 XML:
<root>
<element>
<id>1</id>
</element>
<element>
<id>2</id>
<parentId>1</parentId>
</element>
<element>
<id>3</id>
<parentId>2</parentId>
</element>
<element>
<id>4</id>
<parentId>3</parentId>
</element>
<element>
<id>5</id>
<parentId>2</parentId>
</element>
<element>
<id>6</id>
<parentId>5</parentId>
</element>
</root>
现在,我想为例如元素 3 选择所有“父”节点。假设元素 3 的所需输出应该是:
- 元素 1
- 元素 2
元素 2 的期望输出应该是:
- 元素 1
元素 6 的期望输出应该是
- 要素 5
- 元素 2
- 元素 1
这甚至可以用 XPath 实现吗?如果是,你怎么能做到?
解决方案
这甚至可以用 XPath 实现吗?如果是,你怎么能做到?
一、通用XSLT 1.0解决方案
正如 OA 的评论中所表达的:
“目标是在他们的孩子之前产生父元素。”
这也称为“拓扑排序”
这是我的 XSLT 1.0 拓扑排序实现,日期为 2001 年:
这是 XSLT 拓扑排序的另一种变体,“将派系保持在一起”(稳定拓扑排序)https://www.biglist.com/lists/lists.mulberrytech.com/xsl-list/archives/200112/msg01009。 html
至于使用纯 XPath获取给定元素的隐含层次结构祖先的 ID 序列,下面是使用 XPath 3.0 或更高版本的解决方案。
二、纯 XPath 3 解决方案
此 XPath 3.0 表达式定义了一个内联 (XPath 3.0) 函数,该函数计算元素的祖先路径,作为外部参数 $ pCurrent 传递:
let $pCurrent := current(),
$ancestor-path-inner := function($el as element(), $self as function(*)) as xs:string*
{
let $parent := $el/../element[id eq $el/parentId]
return
if(not(empty($parent))) then $self($parent, $self)
else ()
,
$el/parentId
},
$ancestor-path := function($el as element()) as xs:string*
{ $ancestor-path-inner($el, $ancestor-path-inner)}
return
string-join($ancestor-path($pCurrent), '-')
基于 XSLT 3.0 的验证:
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="element">
<element id="{id}" ancestor-path-ids=
"{let $pCurrent := current(),
$ancestor-path-inner := function($el as element(),
$self as function(*)) as xs:string*
{
let $parent := $el/../element[id eq $el/parentId]
return
if(not(empty($parent))) then $self($parent, $self)
else ()
,
$el/parentId
},
$ancestor-path := function($el as element()) as xs:string*
{ $ancestor-path-inner($el, $ancestor-path-inner)}
return
string-join($ancestor-path($pCurrent), '-')}"/>
</xsl:template>
</xsl:stylesheet>
当此转换应用于提供的 XML 文档时:
<root>
<element>
<id>1</id>
</element>
<element>
<id>2</id>
<parentId>1</parentId>
</element>
<element>
<id>3</id>
<parentId>2</parentId>
</element>
<element>
<id>4</id>
<parentId>3</parentId>
</element>
<element>
<id>5</id>
<parentId>2</parentId>
</element>
<element>
<id>6</id>
<parentId>5</parentId>
</element>
</root>
产生了想要的正确结果:
<element id="1" ancestor-path-ids=""/>
<element id="2" ancestor-path-ids="1"/>
<element id="3" ancestor-path-ids="1-2"/>
<element id="4" ancestor-path-ids="1-2-3"/>
<element id="5" ancestor-path-ids="1-2"/>
<element id="6" ancestor-path-ids="1-2-5"/>
推荐阅读
- angular - 使用反应形式的角度电子邮件验证
- firebase - Flutter,FireStore如何一次更新所有文档中的字段
- c# - 重新排列 csv.file
- java - iText 7(Java)当大表扩展到下一页时,它不绘制底部边框
- javascript - 如何打开动态生成带有 JavaScript 的 HMTL
- java - Android Studio 安装失败
- css - 每个动作都有不同的光标
- java - 在没有给定数字集的情况下找到所有可能的数字组合以达到给定的总和(Java)
- ios - Swift Dispatch Group 问题线程问题
- c# - 如何在datagridview winform中将新添加的行移动到顶部?