Search

I am pulling in data from an external XML source that can't sort by random.

What I'd like to do is show a single random entry from a list of entries on each page.

Can anyone think of an XSL solution to this please?

Thanks

Hi Nick. Thanks for your response and sorry for the delay replying, I've been on holiday.

I have tried all of these techniques and fun into problems with all of them - probably due to my poor understanding of the structure of XSLT.

I've focused on the latter EXSLT solution but don't really understand how it should be structured within my site. So here are a few details:

The XML looks like this:

<site-featured status="fresh" creation="2011-09-19T10:05:18+01:00">
<item>
   <Id>a0AD000000KGyQ6MAL</Id>
   <Name>0</Name>
   <PurpleLocation__c>thailand</PurpleLocation__c>
   <pb__ItemName__c>Pearl of Naithon</pb__ItemName__c>
   <Purchase_Price__c>13000000</Purchase_Price__c>
   <CurrencyIsoCode>THB</CurrencyIsoCode>
</item>
<item>
   <Id>a0AD000000KH8hVMAT</Id>
   <Name>0</Name>
   <PurpleLocation__c>turkey</PurpleLocation__c>
   <pb__ItemName__c>Terrace Life – Gocek</pb__ItemName__c>
   <Purchase_Price__c>99500</Purchase_Price__c>
   <CurrencyIsoCode>GBP</CurrencyIsoCode>
</item>
</site-featured>

The XSLT template looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet 
    version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:exsl="http://exslt.org/common"
    xmlns:math="http://exslt.org/math"
    extension-element-prefixes="exsl" >

<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:template name="rand">

    <xsl:variable name="entries">
            <xsl:for-each select="/data/site-featured/item">
                <entry-container>
                    <xsl:attribute name="random"><xsl:value-of select="floor(math:random() * 10)" /></xsl:attribute>
                    <xsl:copy-of select="." />
                </entry-container>
            </xsl:for-each>
    </xsl:variable>

    <xsl:for-each select="exsl:node-set($entries)/entry-container">
        <xsl:sort select="@random" order="ascending" data-type="number" />      

<xsl:call-template name="featured-sidebar" />

    </xsl:for-each>

</xsl:template>

</xsl:stylesheet>

The template "featured-sidebar" which shows the information for the selected entry looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template name="featured-sidebar">

        <div class="featured">
            <h4>Featured Developments</h4>
            <h5><xsl:value-of select="pb__ItemName__c" />, <span><xsl:value-of select="PurpleLocation__c" /></span></h5>
            <p>From only <xsl:call-template name="currency"><xsl:with-param name="string" select="CurrencyIsoCode"/></xsl:call-template><xsl:value-of select="Purchase_Price__c" /></p> 
                <a class="btn" href="{$root}/properties-for-sale/{PurpleLocation__c}/{Id}/">More info</a>
        </div>

</xsl:template>

</xsl:stylesheet>

I am calling the template from my master.xsl template using:

<xsl:call-template name="rand" />

The result is that I see one instance of the featured-sidebar template but that no data shows up in the template. I have not used EXSLT before so don't know if I need to declare that I am using it in the master.xsl template or only in the specific template that uses the function.

I suspect I have strung things together incorrectly. Am I close?

Thanks. S

Sorry Stuart, I completely forgot about this thread. I can't see anything wrong with your code that would make this happen, so I presume you resolved this in time.

I came across this thread again because I've just needed to implement random sorting in XSLT. There's actually a much simpler way of achieving this without building your XML into a new nodeset and adding a random attribute. Simply sort using the random function!

Include the common and math namespaces as you have done, then your apply templates:

<xsl:apply-templates select="stuff/entry">
    <xsl:sort select="math:random()" data-type="number" order="ascending" />
</xsl:apply-templates>

I didn't think this would work, but it does. Hope that saves a few minutes next time!

Thanks Nick. It actually remains unsolved. I'll try this. It looks like the exact answer I was looking for.

Thanks Nick. This works a treat. I take my hat off once more :-)

Rad.

Thanks, Nick!

Sorry to drag up an old post, I've been racking my brains about this for hours. It doesn't work :(

Here is the code I've got:

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:exsl="http://exslt.org/common"
    xmlns:math="http://exslt.org/math"
    extension-element-prefixes="exsl math">

  <xsl:output method="xml" indent="yes" />

<xsl:variable name="thumbnail-widths">
    <item>120</item>
    <item>240</item>
    <item>360</item>
</xsl:variable>

<xsl:template match="/">
    <xsl:call-template name="find-random-node"/>
</xsl:template>

<xsl:template name="find-random-node">
    <xsl:param name="list" select="exsl:node-set($thumbnail-widths)"/>
    <xsl:for-each select="$list/item">
        <xsl:sort select="math:random()" data-type="number" order="ascending"/>
        <rand><xsl:value-of select="text()"/></rand>
    </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

Every time I run this through xsltproc to test it, it returns the original order! I've also see a post that suggests to do it similar to what Stu originally posted

position() = ceiling(math:random() * count($list/item))

As the random function returns a value between 0 and 1, I wouldn't understand how this is supposed to work anyway.

Any clues anyone?

It might have to do with you stuffing XML in a variable. Try using it on the original XML data.

I figured it out, sorry for not posting.

It turns out that any Math functions in exslt are running Javascript, which xsltproc has no access to on the command line. Weird.

It worked fine when I tested in the browser after about 2 hours of scratching my head on the command line.

What's the syntax to scratch head on Command line? :p

sudo scratchme --wild --often

Ah, and for continuous scratching you can do sudo scratchme -t 300 (time in seconds). There are a lot of params, be sure to check it with the --help option.

Oh. My. God. You're all mental.

Oh. My. God. You're all mental.

Obviously you set your -t to high. Try lowering it and see if that helps.

LOL

It turns out that any Math functions in exslt are running Javascript, which xsltproc has no access to on the command line. Weird.

What?! I highly doubt that (having looked specifically at math:random a few months ago). Can you post the code you were testing on the CLI?

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:exsl="http://exslt.org/common"
    xmlns:math="http://exslt.org/math"
    extension-element-prefixes="exsl math">

<xsl:output method="xml" indent="yes" />

<!--
    Allowed Thumbnail Lengths
    We must limit these to allow correct arranging on the page
    These carry a keyword to assist matching on aspect ratio
-->
<xsl:variable name="thumbnail-lengths">
    <item name="one">120</item>
    <item name="two">240</item>
    <item name="three">360</item>
</xsl:variable>

<xsl:variable name="aspect-ratios">
    <item name="onebyone">1</item>
    <item name="onebytwo">0.5</item>
    <item name="onebythree">0.33333</item>
    <item name="twobyone">2</item>
    <item name="twobytwo">1</item>
    <item name="twobythree">0.66667</item>
    <item name="threebyone">3</item>
    <item name="threebytwo">1.5</item>
    <item name="threebythree">1</item>
</xsl:variable>



<xsl:template match="/">
    <xsl:variable name="aspect-ratio">
        <xsl:call-template name="get-aspect-ratio">
            <xsl:with-param name="width" select="200"/>
            <xsl:with-param name="height" select="221"/>
        </xsl:call-template>
    </xsl:variable>

    <xsl:call-template name="get-random-ratio-name">
        <xsl:with-param name="value" select="$aspect-ratio"/>
    </xsl:call-template>
</xsl:template>



<xsl:template name="get-random-ratio-name">
    <xsl:param name="value"/>
    <xsl:variable name="nodes" select="exsl:node-set($aspect-ratios)/item[text() = $value]"/>
    <xsl:variable name="rand" select="ceiling(math:random() * count($nodes))"/>
    <xsl:value-of select="math:random() * count($nodes)"/>
    <xsl:choose>
        <xsl:when test="not($rand = 'NaN')">
            <!--<xsl:value-of select="$nodes[position() = $rand]/@name"/>-->
        </xsl:when>
        <xsl:otherwise>
            <!--<xsl:value-of select="$nodes[position() = 1]/@name"/>-->
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

<xsl:template name="get-aspect-ratio">
    <xsl:param name="width"/>
    <xsl:param name="height"/>

    <xsl:variable name="ratios" select="exsl:node-set($aspect-ratios)"/>
    <xsl:variable name="ratio-actual" select="$width div $height"/>

    <xsl:for-each select="$ratios/item">
        <xsl:sort select="(. - $ratio-actual) * not(0 > . - $ratio-actual ) - (. - $ratio-actual) * (0 > . - $ratio-actual)"/>
        <xsl:if test="position() = 1">
            <xsl:value-of select="text()"/>
        </xsl:if>
    </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

May have got that statement slightly wrong... I've since discovered it does, but there is still a real problem using it via the cli.

What I found interesting is that no matter how many times I call this transformation via the command line (using an empty xml doc with a root node) and it always returns the same random number, which it shouldn't as it's a random number function...

Hmm...

For data.xml being as simple as

<?xml version="1.0" encoding="UTF-8" ?>
<data />

it looks just fine:

wernerns@ix1127:~/Desktop$ xsltproc style.xsl data.xml 
<?xml version="1.0"?>
2.866987366167357
wernerns@ix1127:~/Desktop$ xsltproc style.xsl data.xml 
<?xml version="1.0"?>
0.45933434993929
wernerns@ix1127:~/Desktop$ xsltproc style.xsl data.xml 
<?xml version="1.0"?>
1.094800775914826
wernerns@ix1127:~/Desktop$ xsltproc style.xsl data.xml 
<?xml version="1.0"?>
0.204256723730013

Create an account or sign in to comment.

Symphony • Open Source XSLT CMS

Server Requirements

  • PHP 5.3-5.6 or 7.0-7.3
  • PHP's LibXML module, with the XSLT extension enabled (--with-xsl)
  • MySQL 5.5 or above
  • An Apache or Litespeed webserver
  • Apache's mod_rewrite module or equivalent

Compatible Hosts

Sign in

Login details