XQuery 替换节点中的多个单词
XQuery replace multiple words from a node
以下是 XML 文件 -
<A>
<B>
<Data>John is a good</Data>
<Data>James loves cricket</Data>
<Data>John loves Hockey</Data>
</B>
<B>
<Data>Stuart loves cricket</Data>
<Data>Johny loves Hockey</Data>
</B>
</A>
我想替换节点 Data
中除姓名(James、John、Stuart)之外的所有单词。我正在尝试一次性完成这项工作。
以下是 XQuery -
for $words in ("Hockey", "crikcet", "is")
let $word := $words
for $x in doc('file')//Data
where contains($x, $word)
return replace value of node $x with normalize-space(replace($x, $word, ''))
我遇到错误-
[XUDY0017] Node can only be replaced once: element Data ...
我正在使用 BaseX 7.6
通过交换两个循环,确保每个节点只被触及一次。这是一种可能 lambda magic 一个接一个地进行所有替换:
let $words := ("Hockey", "cricket", "is")
for $data in doc('file')//Data
where some $word in $words satisfies contains($data, $word)
return
replace value of node $data
with normalize-space(fold-left($words, $data, replace(?, ?, '')))
首先,您应该更新到最新版本的 BaseX。它目前的版本是 8.2.x 和 7.6。很久没支持了
其次,您的整个方法并不特别像 XQuery。例如,而不是做
for $words in ("Hockey", "crikcet", "is")
let $word := $words
下一行的作用完全相同,但更短更容易
for $word in ("Hockey", "crikcet", "is")
由于有两个 for 循环,如果一个数据值包含多个命中,同一个节点可以在这里使用两次或更多次。这是您出现错误消息的时候(在您提供的示例数据集中实际上不会发生这种情况,因为每个数据值只包含一个搜索字符串)。
多次替换每个值不起作用,因为 XQuery 更新根据挂起更新列表 (PUL) 应用更新,即在查询结束时。如果要将同一个数据值替换两次,处理器当然不知道用什么替换。相反,您应该自己计算替换值,然后替换完整值。这是递归进来的,你的用例就是一个很好的例子。因此,以下应该有效:
declare function local:replace-word($word as xs:string, $search as xs:string*, $replace as xs:string*) as xs:string {
if (empty($search)) then $word
else replace(local:replace-word($word, tail($search), tail($replace)), head($search), head($replace))
};
let $words := ("Hockey", "cricket", "is")
let $replace := ("Replace1", "Replace2", "Replace3")
for $x in //Data
return replace value of node $x with normalize-space(local:replace-word($x, $words, $replace))
那么,它有什么作用?首先,我介绍了第二个序列,其中包含您要替换的值。在您的查询中,您总是用一个空字符串替换,即您删除了这个词,这不是您的问题所要求的。此外,我将您的错字替换为 cricket
.
我们现在只有一个 for 循环,它遍历每个 Data
元素。它调用 local:replace-word
函数。此函数调用自身(因此:递归)直到序列中不再有 search/replace 个单词。 head()
获取序列中的第一项,而 last()
获取序列中的所有其他值。
以下是 XML 文件 -
<A>
<B>
<Data>John is a good</Data>
<Data>James loves cricket</Data>
<Data>John loves Hockey</Data>
</B>
<B>
<Data>Stuart loves cricket</Data>
<Data>Johny loves Hockey</Data>
</B>
</A>
我想替换节点 Data
中除姓名(James、John、Stuart)之外的所有单词。我正在尝试一次性完成这项工作。
以下是 XQuery -
for $words in ("Hockey", "crikcet", "is")
let $word := $words
for $x in doc('file')//Data
where contains($x, $word)
return replace value of node $x with normalize-space(replace($x, $word, ''))
我遇到错误-
[XUDY0017] Node can only be replaced once: element Data ...
我正在使用 BaseX 7.6
通过交换两个循环,确保每个节点只被触及一次。这是一种可能 lambda magic 一个接一个地进行所有替换:
let $words := ("Hockey", "cricket", "is")
for $data in doc('file')//Data
where some $word in $words satisfies contains($data, $word)
return
replace value of node $data
with normalize-space(fold-left($words, $data, replace(?, ?, '')))
首先,您应该更新到最新版本的 BaseX。它目前的版本是 8.2.x 和 7.6。很久没支持了
其次,您的整个方法并不特别像 XQuery。例如,而不是做
for $words in ("Hockey", "crikcet", "is")
let $word := $words
下一行的作用完全相同,但更短更容易
for $word in ("Hockey", "crikcet", "is")
由于有两个 for 循环,如果一个数据值包含多个命中,同一个节点可以在这里使用两次或更多次。这是您出现错误消息的时候(在您提供的示例数据集中实际上不会发生这种情况,因为每个数据值只包含一个搜索字符串)。
多次替换每个值不起作用,因为 XQuery 更新根据挂起更新列表 (PUL) 应用更新,即在查询结束时。如果要将同一个数据值替换两次,处理器当然不知道用什么替换。相反,您应该自己计算替换值,然后替换完整值。这是递归进来的,你的用例就是一个很好的例子。因此,以下应该有效:
declare function local:replace-word($word as xs:string, $search as xs:string*, $replace as xs:string*) as xs:string {
if (empty($search)) then $word
else replace(local:replace-word($word, tail($search), tail($replace)), head($search), head($replace))
};
let $words := ("Hockey", "cricket", "is")
let $replace := ("Replace1", "Replace2", "Replace3")
for $x in //Data
return replace value of node $x with normalize-space(local:replace-word($x, $words, $replace))
那么,它有什么作用?首先,我介绍了第二个序列,其中包含您要替换的值。在您的查询中,您总是用一个空字符串替换,即您删除了这个词,这不是您的问题所要求的。此外,我将您的错字替换为 cricket
.
我们现在只有一个 for 循环,它遍历每个 Data
元素。它调用 local:replace-word
函数。此函数调用自身(因此:递归)直到序列中不再有 search/replace 个单词。 head()
获取序列中的第一项,而 last()
获取序列中的所有其他值。