0

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

示例输入:

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

示例输出(所有more-infos 都折叠成一个元素):

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

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

4

3 回答 3

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>
于 2012-09-28T05:18:06.933 回答
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>
于 2012-09-28T05:29:54.100 回答
0

我带来了:

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

其中$nodes包含输入文档。

它使用 agroup 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 bysheningan 的元素节点。

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似乎是非常合适的。

于 2019-07-04T18:28:58.043 回答