XQuery:合并相同名称的节点

时间:2012-09-28 04:34:31

标签: xquery

我如何获取同名的所有元素节点,并将它们组合成一个保留每个节点的子元素的节点?

示例输入:

<topic>
  <title />
  <language />
  <more-info>
    <itunes />
  </more-info>
  <more-info>
    <imdb />
  </more-info>
  <more-info>
    <netflix />
  </more-info>
</topic>

示例输出(所有more-info都折叠为单个元素):

<topic>
  <title />
  <language />
  <more-info>
    <itunes />
    <imdb />
    <netflix />
  </more-info>
</topic>

编辑:我正在寻找一种方法来做到这一点,而不知道哪些节点名称重新出现。因此,通过上面的示例,我无法使用仅定位more-info的脚本,因为可能还有其他元素也需要应用相同的过程。

3 个答案:

答案 0 :(得分:1)

使用

declare option saxon:output "omit-xml-declaration=yes";
<topic>
  <title />
  <language />
  <more-info>
   {for $inf in /*/more-info/node()
     return $inf
   }
  </more-info>
</topic>

将此XQuery应用于提供的XML文档

<topic>
  <title />
  <language />
  <more-info>
    <itunes />
  </more-info>
  <more-info>
    <imdb />
  </more-info>
  <more-info>
    <netflix />
  </more-info>
</topic>

产生了想要的正确结果

<topic>
   <title/>
   <language/>
   <more-info>
      <itunes/>
      <imdb/>
      <netflix/>
   </more-info>
</topic>

答案 1 :(得分:0)

如果您可以使用它,这对XSLT来说似乎更好。

XML输入

<topic>
    <title />
    <language />
    <more-info>
        <itunes />
    </more-info>
    <more-info>
        <imdb />
    </more-info>
    <more-info>
        <netflix />
    </more-info>
</topic>

XSLT 2.0

<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="/*">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:for-each-group select="*" group-by="name()">
                <xsl:copy>
                    <xsl:apply-templates select="current-group()/@*"/>
                    <xsl:apply-templates select="current-group()/*"/>
                </xsl:copy>
            </xsl:for-each-group>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

XML输出

<topic>
   <title/>
   <language/>
   <more-info>
      <itunes/>
      <imdb/>
      <netflix/>
   </more-info>
</topic>

答案 2 :(得分:0)

我是这么做的:

for $n in $nodes/node()
let $lname := local-name($n)
group by $lname
return element {$lname} {
  $n/node()
}

$nodes包含输入文档。

它使用group by$n变量绑定到分组节点列表。 因此,表达式$n/node()代表节点序列。

要使其递归,我们必须声明一个函数并调用它:

declare function local:recurse($node){
  for $n in $node/text() return $n,
  for $n in $node/element()
  let $lname := local-name($n)
  group by $lname
  return element {$lname} {
    for $m in $n return local:recurse($m)
  }
};

local:recurse($nodes)

第一行以逗号结尾。这是一个列表串联。因此,我们首先输出文本节点,然后输出具有上述group by sheningan的元素节点。

XML输入

<topic>
  <title>Test</title>
  <language />
  <more-info>
    <itunes>
      <playlist>
        <item>2</item>
      </playlist>
      <playlist>
        <item>3</item>
      </playlist>
    </itunes>
  </more-info>
  <more-info>
    <imdb>Imdb info</imdb>
  </more-info>
  <more-info>
    <netflix>Netflix info</netflix>
  </more-info>
</topic>

XML输出

<title>Test</title>
<language/>
<more-info>
  <itunes>
    <playlist>
      <item>2</item>
      <item>3</item>
    </playlist>
  </itunes>
  <imdb>Imdb info</imdb>
  <netflix>Netflix info</netflix>
</more-info>

备注

我不知道为什么XSLT更容易。也许apply-templates伪装了递归,使其不那么令人生畏。

此外,与要求在“循环”内进行匹配的XQuery相比,匹配在“循环”外进行声明这一事实使它更容易(然后必须与模式配对以进行完全控制)。

无论如何,在这个特殊的示例中,XQuery似乎非常合适。