XQuery从父级删除节点并将其作为兄弟插入

时间:2013-11-16 05:02:11

标签: xml xquery

以下是XML结构 -

<Docs>
 <Doc>
   <P>blah blah blah<pg>1</pg>blah blah</P>
   <P>blah blah blah<pg>2</pg>blah blah</P>
 </Doc>
 <Doc>
   <P>blah blah blah<pg>3</pg>blah blah</P>
   <P>blah blah blah<pg>4</pg>blah blah</P>
 </Doc>
</Docs>

我想删除pg节点中的P节点,并将其插入P节点的兄弟节点。 像这样 -

<Docs>
 <Doc>
   <P>blah blah blah</P>
   <pg>1</pg>
   <P>blah blah</P>
   <P>blah blah blah</P>
   <pg>2</pg>
   <P>blah blah</P>
 </Doc>
 <Doc>
   <P>blah blah blah</P>
   <pg>3</pg>
   <P>blah blah</P>
   <P>blah blah blah</P>
   <pg>4</pg>
   <P>blah blah</P>
 </Doc>
</Docs>

如何完成它?

4 个答案:

答案 0 :(得分:3)

这是一个XSLT选项...

XML输入

<Docs>
    <Doc>
        <P>blah blah blah<pg>1</pg>blah blah</P>
        <P>blah blah blah<pg>2</pg>blah blah</P>
    </Doc>
    <Doc>
        <P>blah blah blah<pg>3</pg>blah blah</P>
        <P>blah blah blah<pg>4</pg>blah blah</P>
    </Doc>
</Docs>

XSLT 1.0

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="P">
        <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="P/text()">
        <P><xsl:value-of select="."/></P>
    </xsl:template>

</xsl:stylesheet>

XML输出

<Docs>
   <Doc>
      <P>blah blah blah</P>
      <pg>1</pg>
      <P>blah blah</P>
      <P>blah blah blah</P>
      <pg>2</pg>
      <P>blah blah</P>
   </Doc>
   <Doc>
      <P>blah blah blah</P>
      <pg>3</pg>
      <P>blah blah</P>
      <P>blah blah blah</P>
      <pg>4</pg>
      <P>blah blah</P>
   </Doc>
</Docs>

答案 1 :(得分:2)

如果您支持XQuery Update,这是一个XQuery解决方案。

for $p in $c//P                       (: for each paragraph tag :)
return (
  for $node in $p/(text(), pg)        (: find all subnodes :)
  return (
    let $node :=
      if ($node/self::text())
      then element P { $node }        (: wrap text nodes in new paragraph tags :)
      else $node
    return insert node $node after $p (: insert the node after the old paragraph tag :)
  ),
  delete node $p                      (: drop the old paragraph tag :)
)

刚刚实现了一个版本没有 XQuery Update(只返回结果)更短:

element Docs {
  element Doc {
    for $node in //P/(text(), pg)
    return
      if ($node/self::text())
      then element P { $node }
      else $node
  }
}

答案 2 :(得分:2)

XSLT看起来确实更适合这项任务。 XSLT 2.0提供了xsl:for-each-group功能,在这里非常有用。它比仅仅匹配P内的text()节点更健壮。如果P元素在pg标记旁边包含其他内联元素,那肯定会有所帮助。

这是2.0解决方案,Daniel Haley稍微改变了解决方案:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="P">
        <xsl:variable name="P" select="."/>
        <xsl:for-each-group select="node()" group-starting-with="pg">
            <xsl:apply-templates select="self::pg"/>
            <xsl:element name="{node-name($P)}">
                <xsl:apply-templates select="$P/@*|current-group()[not(self::pg)]"/>
            </xsl:element>
        </xsl:for-each-group>
    </xsl:template>

</xsl:stylesheet>

HTH!

答案 3 :(得分:1)

另一方面,XQuery 3.0提供了新的翻滚窗口功能,有助于模仿我在其他答案中描述的for-each-groups行为。这是它的样子:

xquery version "3.0";

let $xml :=
  <Docs>
    <Doc>
        <P>blah blah blah<pg>1</pg>blah blah</P>
        <P>blah blah blah<pg>2</pg>blah blah</P>
    </Doc>
    <Doc>
        <P>blah blah blah<pg>3</pg>blah blah</P>
        <P>blah blah blah<pg>4</pg>blah blah</P>
    </Doc>
  </Docs>
return
  <Docs>{
    for $doc in $xml/Doc
    return
        <Doc>{
            for $P in $doc/P
            return
                for tumbling window $w in $P/node()
                start when true()
                end next $e
                    when $e instance of element(pg)
                return (
                    $w[self::pg],
                    <P>{
                        $w[not(self::pg)]
                    }</P>
                )
        }</Doc>
  }</Docs>

它确实需要一个支持3.0的XQuery处理器,包括这些翻滚窗口。 Zorba是一个很好的例子,您可以在http://try.zorba.io

在线测试此代码

HTH!