【问题标题】:How can I insert elements into my current XML document using XQuery?如何使用 XQuery 将元素插入到当前的 XML 文档中?
【发布时间】:2014-11-10 17:47:41
【问题描述】:

在我当前的 XML 文档中,有一些特定的、特定的原子文本,需要在其周围包装一个新元素。

这是我当前 XML 的 sn-p:

<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>

这是我的 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 中,我添加了几个 &lt;span&gt; 标记,这些标记应保留为标记文本,但是从下面的答案中使用的 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)

这会将 &lt;span&gt; 文本放置在 &lt;untagged&gt; 元素内,而它应该保持包裹在 &lt;span&gt; 元素内。

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

【问题讨论】:

    标签: xml xpath tags xquery


    【解决方案1】:

    使用递归。递归类型切换是一种遍历树的常见模式,并允许您在此过程中进行更改。这是在 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 Update Facility 或使用特定于数据库实现的更新功能来选择和更新您想要的特定节点(类似于您的 for 循环)。但是,可能存在一些问题,因为数据库需要您的更新与事务很好地配合。

    【讨论】:

      猜你喜欢
      • 2018-04-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-31
      • 1970-01-01
      相关资源
      最近更新 更多