基于另一个节点对XML节点进行排序

时间:2013-04-12 10:31:35

标签: xml sorting xslt

在XSLT中,我想调用一个模板,并将一些内容传递给模板。但是,内容应按照视频的排序方式(13-41-61)排序,而不是按照内容排序的顺序排序(61-41-13)。

我有以下XML:

<videos>
    <video key="13" />
    <video key="41" />
    <video key="61" />
</videos>


<contents>
    <content key="61" />
    <content key="41" />
    <content key="13" />
    <content key="10" />
</contents>

XSLT:

<xsl:call-template name="video">
    <xsl:with-param name="content" select="contents/content[@key = videos/video/@key]" />
</xsl:call-template>

有没有办法轻松实现这一目标?

3 个答案:

答案 0 :(得分:1)

此转换似乎是当前发布的解决方案中效率最高的 - 没有count(preceding-sibling::*),没有//content[@key=$key] - 两者都导致O(N ^ 2) - 二次时间复杂度

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>
 <xsl:key name="kContByAtttr" match="content" use="@key"/>
 <xsl:key name="kVidByAtttr" match="video" use="@key"/>

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

 <xsl:template match="contents">
  <contents>
   <xsl:for-each select="/*/videos/video">
     <xsl:apply-templates select="key('kContByAtttr', @key)"/>
   </xsl:for-each>
   <xsl:apply-templates select="*[not(key('kVidByAtttr', @key))]"/>
  </contents>
 </xsl:template>
</xsl:stylesheet>

当应用于提供的XML(包装成单个顶部元素以成为)文档时:

<t>
    <videos>
        <video key="13" />
        <video key="41" />
        <video key="61" />
    </videos>
    <contents>
        <content key="61" />
        <content key="41" />
        <content key="13" />
        <content key="10" />
    </contents>
</t>

生成想要的正确结果

<t>
   <videos>
      <video key="13"/>
      <video key="41"/>
      <video key="61"/>
   </videos>
   <contents>
      <content key="13"/>
      <content key="41"/>
      <content key="61"/>
      <content key="10"/>
   </contents>
</t>

答案 1 :(得分:0)

复制并粘贴我经常使用的一段代码,它应该可以正常工作

<xsl:template match="/">
  <contents>
    <xsl:for-each select="//video">
      <xsl:call-template name="sortedId">
        <xsl:with-param name="key" select="@key"></xsl:with-param>
      </xsl:call-template>
    </xsl:for-each>
  </contents>    
</xsl:template>


<xsl:template name="sortedId" >
    <xsl:param name="key"></xsl:param>
    <xsl:apply-templates select="//content[@key=$key]" />
</xsl:template>

<xsl:template match="content">
    <xsl:copy-of select="." />
</xsl:template>

结果是:

<contents>
  <content key="13" />
  <content key="41" />
  <content key="61" />
</contents>

答案 2 :(得分:0)

您需要使用<xsl:call-template>的具体原因吗?如果您只想按<content>元素所在的顺序对<video>元素进行排序,则可以执行以下操作:

样式表

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"
    omit-xml-declaration="yes"/>
  <xsl:strip-space elements="*"/>

  <!--
  Index <video> elements according to their position in the tree.
  See http://stackoverflow.com/a/5876074/825783
  -->
  <xsl:key name="kVideo" match="video"
    use="count(preceding-sibling::video) + 1"/>

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

  <xsl:template match="content">
    <xsl:copy>
      <!--
      Apply attributes (fallback to account for the extra <content> element)
      -->
      <xsl:apply-templates select="@*"/>
      <!--
      Apply the @key attribute of the <video> element that's in a
      position corresponding to the position of this <content> element
      -->
      <xsl:apply-templates select="key('kVideo', position())/@key"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

输入

<root>
  <videos>
    <video key="13"/>
    <video key="41"/>
    <video key="61"/>
  </videos>
  <contents>
    <content key="61"/>
    <content key="41"/>
    <content key="13"/>
    <content key="10"/>
  </contents>
</root>

输出

<root>
  <videos>
    <video key="13"/>
    <video key="41"/>
    <video key="61"/>
  </videos>
  <contents>
    <content key="13"/>
    <content key="41"/>
    <content key="61"/>
    <content key="10"/>
  </contents>
</root>