如何使用XQuery将元素插入到当前的XML文档中?

时间:2014-11-10 17:47:41

标签: xml xpath tags xquery

在我当前的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的未标记文本并应用新元素 - 但是无法追加新元素一个完整的输出。

以下是我当前的不受欢迎的输出:

&#13;
&#13;
<untagged>THIS IS UNTAGGED TEXT</untagged>
<untagged>THIS IS UNTAGGED TEXT</untagged>
&#13;
&#13;
&#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>元素内。

我认为错误在于条件语句,如何改进?

1 个答案:

答案 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循环)。然而,可能存在问题,因为数据库将要求您的更新与事务完美匹配。