XPath 3.0 递归查询
XPath 3.0 Recursive Query
我有一些 XML 具有类似继承语义的数据,我想进行一个将继承考虑在内的查询。我知道这在 XPath 1.0 中是不可能的,但我相信在 XPath 3.0 中是可能的,但我对 3.0 不熟悉。
所以我的结构是这样的:
<elems>
<elem id="n">
<property name="xxx" value="yyy"/>
...
</elem>
</elems>
不是,名字为inherits
的属性指向另一个<elem>
的@id
。所以,基本上,我想查询 <elem>
的 @id
有(或没有)一个 属性 Z,那个 属性 是在它自己上还是在任何通过 inherits
属性 链接的元素的数量。例如:
<elems>
<elem id="1">
<property name="a" value="alpha"/>
</elem>
<elem id="2">
<property name="inherits" value="1"/>
<property name="b" value="bravo"/>
</elem>
<elem id="3">
<property name="inherits" value="2"/>
<property name="c" value="charlie"/>
</elem>
</elems>
因此,对具有 属性 c
的元素的查询将 return 3
,其反向将 return 1
和 2
。查询具有 属性 b
的元素将 return 2
和 3
及其反向将 return 1
。最后,对具有 属性 a
的元素的调用将 return 1
、2
和 3
,相反则不会 return 随便什么。
我该怎么做?
你要找的本质上是一个传递闭包,这是最常见的递归查询类型;基本上 XPath 不能进行递归查询,除了内置的祖先轴和后代轴的特殊情况。
XPath 3.0 允许您定义函数,但因为它们是匿名的,所以它们不能(轻易地)调用自己。
“(easily)”是因为有一个免责条款:显然 Y 组合器可以让你克服这个限制。例如参见 [=11=].
这是一个纯 XPath 3.1 解决方案:
下面的函数$allProps(),returns一个字符串序列,是所有属性的名称id 等于传递给函数的 $id 参数的元素。
在此示例表达式中,函数 $allProps() 被调用 3 次——每个 "elem" 元素调用一次,返回的属性由 NL 字符分隔:
let $root := /,
$allProps-inner := function($id as xs:integer, $self as function(*)) as xs:string*
{
let $elem := $root/*/elem[xs:integer(@id )eq $id],
$ownProperties := $elem/property/@name[not(. eq 'inherits')]/string(),
$ParentId := $elem/property[@name eq 'inherits']/@value
return
(
$ownProperties,
if(empty($ParentId)) then ()
else
$self($ParentId, $self)
)
},
$allProps := function($id as xs:integer) as xs:string*
{ $allProps-inner($id, $allProps-inner ) }
return
(
$allProps(1), '
',
$allProps(2), '
',
$allProps(3), '
'
)
基于 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">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:value-of select=
"let $root := /,
$allProps-inner := function($id as xs:integer, $self as function(*)) as xs:string*
{
let $elem := $root/*/elem[xs:integer(@id )eq $id],
$ownProperties := $elem/property/@name[not(. eq 'inherits')]/string(),
$ParentId := $elem/property[@name eq 'inherits']/@value
return
(
$ownProperties,
if(empty($ParentId)) then ()
else
$self($ParentId, $self)
)
},
$allProps := function($id as xs:integer) as xs:string*
{ $allProps-inner($id, $allProps-inner ) }
return
(
$allProps(1), '
',
$allProps(2), '
',
$allProps(3), '
'
)
"/>
</xsl:template>
</xsl:stylesheet>
当此转换应用于提供的 XML 文档时:
<elems>
<elem id="1">
<property name="a" value="alpha"/>
</elem>
<elem id="2">
<property name="inherits" value="1"/>
<property name="b" value="bravo"/>
</elem>
<elem id="3">
<property name="inherits" value="2"/>
<property name="c" value="charlie"/>
</elem>
</elems>
产生了想要的、正确的结果:
a
b a
c b a
最后自然得出原题的解法:
So a query for elements with property c would return 3, and its
reverse would return 1 and 2. A query for elements with property b
would return 2 and 3 and its reverse would return 1. Finally, a call
for elements with property a would return 1, 2 and 3, and it's reverse
would not return anything.
How do I do that?
let $root := /,
$allProps-inner := function($id as xs:integer, $self as function(*)) as xs:string*
{
let $elem := $root/*/elem[xs:integer(@id )eq $id],
$ownProperties := $elem/property/@name[not(. eq 'inherits')]/string(),
$ParentId := $elem/property[@name eq 'inherits']/@value
return
(
$ownProperties,
if(empty($ParentId)) then ()
else
$self($ParentId, $self)
)
},
$allProps := function($id as xs:integer) as xs:string*
{ $allProps-inner($id, $allProps-inner ) }
return
(
for $name in ('a', 'b', 'c')
return
( $root/*/elem[$name = $allProps(@id) ]/@id, '
' )
)
当这个XPath表达式被求值时(只需将转换中的XPath表达式替换为这个),那么输出的结果就是想要的,正确的一个:
1 2 3
2 3
3
我有一些 XML 具有类似继承语义的数据,我想进行一个将继承考虑在内的查询。我知道这在 XPath 1.0 中是不可能的,但我相信在 XPath 3.0 中是可能的,但我对 3.0 不熟悉。
所以我的结构是这样的:
<elems>
<elem id="n">
<property name="xxx" value="yyy"/>
...
</elem>
</elems>
不是,名字为inherits
的属性指向另一个<elem>
的@id
。所以,基本上,我想查询 <elem>
的 @id
有(或没有)一个 属性 Z,那个 属性 是在它自己上还是在任何通过 inherits
属性 链接的元素的数量。例如:
<elems>
<elem id="1">
<property name="a" value="alpha"/>
</elem>
<elem id="2">
<property name="inherits" value="1"/>
<property name="b" value="bravo"/>
</elem>
<elem id="3">
<property name="inherits" value="2"/>
<property name="c" value="charlie"/>
</elem>
</elems>
因此,对具有 属性 c
的元素的查询将 return 3
,其反向将 return 1
和 2
。查询具有 属性 b
的元素将 return 2
和 3
及其反向将 return 1
。最后,对具有 属性 a
的元素的调用将 return 1
、2
和 3
,相反则不会 return 随便什么。
我该怎么做?
你要找的本质上是一个传递闭包,这是最常见的递归查询类型;基本上 XPath 不能进行递归查询,除了内置的祖先轴和后代轴的特殊情况。
XPath 3.0 允许您定义函数,但因为它们是匿名的,所以它们不能(轻易地)调用自己。
“(easily)”是因为有一个免责条款:显然 Y 组合器可以让你克服这个限制。例如参见 [=11=].
这是一个纯 XPath 3.1 解决方案:
下面的函数$allProps(),returns一个字符串序列,是所有属性的名称id 等于传递给函数的 $id 参数的元素。
在此示例表达式中,函数 $allProps() 被调用 3 次——每个 "elem" 元素调用一次,返回的属性由 NL 字符分隔:
let $root := /,
$allProps-inner := function($id as xs:integer, $self as function(*)) as xs:string*
{
let $elem := $root/*/elem[xs:integer(@id )eq $id],
$ownProperties := $elem/property/@name[not(. eq 'inherits')]/string(),
$ParentId := $elem/property[@name eq 'inherits']/@value
return
(
$ownProperties,
if(empty($ParentId)) then ()
else
$self($ParentId, $self)
)
},
$allProps := function($id as xs:integer) as xs:string*
{ $allProps-inner($id, $allProps-inner ) }
return
(
$allProps(1), '
',
$allProps(2), '
',
$allProps(3), '
'
)
基于 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">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:value-of select=
"let $root := /,
$allProps-inner := function($id as xs:integer, $self as function(*)) as xs:string*
{
let $elem := $root/*/elem[xs:integer(@id )eq $id],
$ownProperties := $elem/property/@name[not(. eq 'inherits')]/string(),
$ParentId := $elem/property[@name eq 'inherits']/@value
return
(
$ownProperties,
if(empty($ParentId)) then ()
else
$self($ParentId, $self)
)
},
$allProps := function($id as xs:integer) as xs:string*
{ $allProps-inner($id, $allProps-inner ) }
return
(
$allProps(1), '
',
$allProps(2), '
',
$allProps(3), '
'
)
"/>
</xsl:template>
</xsl:stylesheet>
当此转换应用于提供的 XML 文档时:
<elems>
<elem id="1">
<property name="a" value="alpha"/>
</elem>
<elem id="2">
<property name="inherits" value="1"/>
<property name="b" value="bravo"/>
</elem>
<elem id="3">
<property name="inherits" value="2"/>
<property name="c" value="charlie"/>
</elem>
</elems>
产生了想要的、正确的结果:
a
b a
c b a
最后自然得出原题的解法:
So a query for elements with property c would return 3, and its reverse would return 1 and 2. A query for elements with property b would return 2 and 3 and its reverse would return 1. Finally, a call for elements with property a would return 1, 2 and 3, and it's reverse would not return anything.
How do I do that?
let $root := /,
$allProps-inner := function($id as xs:integer, $self as function(*)) as xs:string*
{
let $elem := $root/*/elem[xs:integer(@id )eq $id],
$ownProperties := $elem/property/@name[not(. eq 'inherits')]/string(),
$ParentId := $elem/property[@name eq 'inherits']/@value
return
(
$ownProperties,
if(empty($ParentId)) then ()
else
$self($ParentId, $self)
)
},
$allProps := function($id as xs:integer) as xs:string*
{ $allProps-inner($id, $allProps-inner ) }
return
(
for $name in ('a', 'b', 'c')
return
( $root/*/elem[$name = $allProps(@id) ]/@id, '
' )
)
当这个XPath表达式被求值时(只需将转换中的XPath表达式替换为这个),那么输出的结果就是想要的,正确的一个:
1 2 3
2 3
3