Reputation: 833
In XSLT I'd like to call a template, and passing in some content to a template. The content, however, should be sorted in the way the videos are sorted (13-41-61) rather than in the order the contents are sorted (61-41-13).
I have the following 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>
Is there a way to accomplish this easily?
Upvotes: 2
Views: 89
Reputation: 243479
This transformation seems to be the most efficient of the currently posted solutions -- no count(preceding-sibling::*)
, and no //content[@key=$key]
-- both of which result in O(N^2) -- quadratical time complexity:
<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>
When applied on the provided XML (wrapped into a single top element to become a) document:
<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>
produces the wanted, correct result:
<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>
Upvotes: 1
Reputation: 2585
Is there a specific reason you need to use <xsl:call-template>
? If you just want to sort the <content>
elements in the order the <video>
elements are in, you could do something like this:
<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>
Upvotes: 0
Reputation: 822
Copying and pasting a snippet of code I usually use, it should work
<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>
Result is:
<contents>
<content key="13" />
<content key="41" />
<content key="61" />
</contents>
Upvotes: 0