在我当前的XML文档中,有一些特定的原子文本要求新元素围绕它包装。
以下是我当前XML的片段:
<html n1="namespace1" n2="namespace2">
<head>
<title>Document Title</title>
</head>
<body>
THIS IS UNTAGGED TEXT
<n1:a>
<n1:b>
<n1:c name="attribute1" attribute2="attribute2">
THIS IS TAGGED TEXT
<span class="asd">THIS IS TAGGED TEXT
<span class="xyz">THIS IS TAGGED TEXT</span>
</span>
</n1:c>
THIS IS UNTAGGED TEXT
<n1:d name="attributeA" attribute2="attributeB">
THIS IS TAGGED TEXT
</n1:d>
</n1:b>
</n1:a>
</body>
</html>
以下是最终产品:
<html n1="namespace1" n2="namespace2">
<head>
<title>Document Title</title>
</head>
<body>
<untagged>THIS IS UNTAGGED TEXT</untagged>
<n1:a>
<n1:b>
<n1:c name="attribute1" attribute2="attribute2">
THIS IS TAGGED TEXT
<span class="asd">THIS IS TAGGED TEXT
<span class="xyz">THIS IS TAGGED TEXT</span>
</span>
</n1:c>
<untagged>THIS IS UNTAGGED TEXT</untagged>
<n1:d name="attributeA" attribute2="attributeB">
THIS IS TAGGED TEXT</n1:d>
</n1:b>
</n1:a>
</body>
</html>
我认为这样做的最好方式是通过IF声明;我已经定义了IF语句的标准 - 即我能够提取来自XML的未标记文本并应用新元素 - 但是无法追加新元素一个完整的输出。
以下是我当前的不受欢迎的输出:
<untagged>THIS IS UNTAGGED TEXT</untagged>
<untagged>THIS IS UNTAGGED TEXT</untagged>
&#13;
这是我的XQuery。
declare namespace n1="namespace1"
for $tag in /html/body//*/text()
return
if (
(
fn:namespace-uri($tag/parent::node()) = "namespace1"
and not(exists($tag/parent::node()/attribute::name))
or fn:namespace-uri($tag/parent::node()) != "namespace1"
)
and fn:normalize-space($tag) != ""
)
then <untagged>{$tag}</untagged>
else $tag
IF语句是正确的,它返回任何文本: a)属于命名空间但没有名称属性或 b)不属于命名空间
我的问题是,如何在保留原始XML结构并打印原始节点的同时附加和打印新节点?
更新
在上面的XML中,我添加了几个<span>
标签,这些标签应保留为标记文本,但从下面的答案中使用的XQuery会将其标记为未标记。
这是使用的新XQuery:
declare function local:do(
$n as node()
) as node()*
{
typeswitch($n)
case element() return element { node-name($n) } {
for $child in $n/(@* | node())
return local:do($child)
}
case text() return
if ((fn:namespace-uri( $n/parent::node() ) != "namespace1"
(: *** recursive loop here? ***:)
and fn:normalize-space($n) != "")
or(fn:namespace-uri( $n/parent::node() ) = "namespace1"
and not( exists( $n/parent::node()/attribute::name) )
and fn:normalize-space($n) != "")
)
then element untagged { $n }
else $n
default return $n
};
local:do($xml)
这会将<span>
文本置于<untagged>
元素内,并将其保留在<span>
元素内。
我认为错误在于条件语句,如何改进?
答案 0 :(得分:2)
使用递归。递归类型切换是遍历树的常见模式,允许您沿途进行更改。这是在XQuery中执行类似XSLT的好方法。
declare function local:do(
$n as node()
) as node()*
{
typeswitch ($n)
case element() return element { node-name($n) } {
for $child in $n/(@* | node())
return local:do($child)
}
case text() return
if ((fn:namespace-uri($n/parent::node()) = "namespace1"
and not(exists($n/parent::node()/attribute::name))
or fn:namespace-uri($n/parent::node()) != "namespace1")
and fn:normalize-space($n) != "")
then element untagged { $n }
else $n
default return $n
};
local:do($xml)
或者,如果此文档位于数据库中,您可以使用XQuery更新工具或数据库实现特定的更新功能,仅选择和更新所需的特定节点(类似于for循环)。然而,可能存在问题,因为数据库将要求您的更新与事务完美匹配。