XSLT generate UUID
Asked Answered
D

7

16

How can I generate a UUID with pure XSLT? Basically looking for a way to create unique sequences with XSLT. The sequence can be any length.

I'm using XSLT 2.0.

Daff answered 14/11, 2011 at 19:29 Comment(1)
possible duplicate of Generate GUID in XSLTMobocracy
L
4

Since XSLT is a functional language, generating random numbers is not part of the language. That said, there are extension packages (EXSLT) and some processors (Saxon) that support generation of random numbers. If you can't use extensions or Saxon, then I believe you're out of luck.

Longwood answered 14/11, 2011 at 19:48 Comment(8)
How does Saxon support generation of random number?Daff
Saxon comes with the EXSLT random module built-in. See saxonica.com/documentation/extensions/intro.xmlLongwood
Thank you. Too bad the built in EXSLT modules are not available for the Saxon HE (Home Edition).Daff
Not really out of luck... it's just less convenient. Most random number generators are written in software anyway. So if CPU performance is not a bottleneck, you can implement standard random number generation algorithms in pure, standard XSLT.Melville
E.g. Braaksma's timestamp-based implementation (stylusstudio.com/xsllist/200703/post40430.html) uses only one extension function, math:power(). And math:power() is implemented (as a template) using plain XSLT, here: exslt.org/math/functions/power/math.power.template.xslMelville
There is a short XPath expression that produces a GUID: https://mcmap.net/q/716911/-xslt-generate-uuidBiagio
Was that available in 2011?Longwood
@JimGarrison Yes, see: web.archive.org/web/2011*/uuid.org "Saved 16 times between February 20, 2001 and December 13, 2011."Biagio
U
16

Here's a good example. Basically you set up an extension that points to the java UUID class, and then reference it in the XSL:

<xsl:stylesheet version="2.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:uuid="java:java.util.UUID">
<xsl:template match="/">
  <xsl:variable name="uid" select="uuid:randomUUID()"/>
  <xsl:value-of select="$uid"/>
</xsl:template>
Uncork answered 14/11, 2011 at 19:34 Comment(3)
Sorry, for not clarifying. I did see that example before but it needs to be done in pure XSLT. No Java.Daff
I've upvoted as this worked for me. However, it's worth noting that reflexive java calls like this are also unavailable for Saxon HE licenses (although as others have noted, it's more or less a solved problem to write your own functions to do it).Nihility
PS - never mind xsl:stylesheet/xsl:value-of being invalid, it's not even well formed without a closing xsl:stylesheet tag ;) - I'm sure we can all work out what is meant, though!Nihility
E
7

You can use xslt snippet for this (source: http://code.google.com/p/public-contracts-ontology/source/browse/transformers/GB-notices/uuid.xslt?r=66e1d39a1c140079a86d219df5b3e031007cc957):

<xsl:stylesheet xmlns:uuid="http://www.uuid.org" xmlns:math="http://exslt.org/math" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">

        <xsl:template match="/">
                <xsl:value-of select="
concat('First random ID:', uuid:get-id()),
concat('Base timestamp: ', uuid:generate-timestamp()),
concat('Clock id: ' ,uuid:generate-clock-id()),
concat('Network node: ' ,uuid:get-network-node()),
concat('UUID Version: ' ,uuid:get-uuid-version()),
concat('Generated UUID: ' ,uuid:get-uuid()),
concat('Generated UUID: ' ,uuid:get-uuid()),
concat('Generated UUID: ' ,uuid:get-uuid()),
concat('Generated UUID: ' ,uuid:get-uuid())
" separator="&#10;"/>
        </xsl:template>

    <!--
Functions in the uuid: namespace are used to calculate a UUID
The method used is a derived timestamp method, which is explained
here: http://www.famkruithof.net/guid-uuid-timebased.html
and here: http://www.ietf.org/rfc/rfc4122.txt
-->
    <!--
Returns the UUID
-->
    <xsl:function name="uuid:get-uuid" as="xs:string*">
        <xsl:variable name="ts" select="uuid:ts-to-hex(uuid:generate-timestamp())"/>
        <xsl:value-of separator="-" select="
            substring($ts, 8, 8),
            substring($ts, 4, 4),
            string-join((uuid:get-uuid-version(), substring($ts, 1, 3)), ''),
            uuid:generate-clock-id(),
            uuid:get-network-node()"/>
    </xsl:function>
    <!--
internal aux. fu
with saxon, this creates a more-unique result with
generate-id then when just using a variable containing a node
-->
    <xsl:function name="uuid:_get-node">
        <xsl:comment/>
    </xsl:function>
    <!-- generates some kind of unique id -->
    <xsl:function name="uuid:get-id" as="xs:string">
        <xsl:sequence select="generate-id(uuid:_get-node())"/>
    </xsl:function>
    <!--
should return the next nr in sequence, but this can't be done
in xslt. Instead, it returns a guaranteed unique number
-->
    <xsl:function name="uuid:next-nr" as="xs:integer">
        <xsl:variable name="node">
            <xsl:comment/>
        </xsl:variable>
        <xsl:sequence select="
            xs:integer(replace(
            generate-id($node), '\D', ''))"/>
    </xsl:function>
    <!-- internal fu for returning hex digits only -->
    <xsl:function name="uuid:_hex-only" as="xs:string">
        <xsl:param name="string"/>
        <xsl:param name="count"/>
        <xsl:sequence select="
            substring(replace(
            $string, '[^0-9a-fA-F]', '')
            , 1, $count)"/>
    </xsl:function>
    <!-- may as well be defined as returning the same seq each time -->
    <xsl:variable name="_clock" select="uuid:get-id()"/>
    <xsl:function name="uuid:generate-clock-id" as="xs:string">
        <xsl:sequence select="uuid:_hex-only($_clock, 4)"/>
    </xsl:function>
    <!--
returns the network node, this one is 'random', but must
be the same within calls. The least-significant bit must be '1'
when it is not a real MAC address (in this case it is set to '1')
-->
    <xsl:function name="uuid:get-network-node" as="xs:string">
        <xsl:sequence select="uuid:_hex-only('09-17-3F-13-E4-C5', 12)"/>
    </xsl:function>
    <!-- returns version, for timestamp uuids, this is "1" -->
    <xsl:function name="uuid:get-uuid-version" as="xs:string">
        <xsl:sequence select="'1'"/>
    </xsl:function>
    <!--
Generates a timestamp of the amount of 100 nanosecond
intervals from 15 October 1582, in UTC time.
-->
    <xsl:function name="uuid:generate-timestamp">
        <!--
date calculation automatically goes
correct when you add the timezone information, in this
case that is UTC.
-->
        <xsl:variable name="duration-from-1582" as="xs:dayTimeDuration">
            <xsl:sequence select="
                current-dateTime() -
                xs:dateTime('1582-10-15T00:00:00.000Z')"/>
        </xsl:variable>
        <xsl:variable name="random-offset" as="xs:integer">
            <xsl:sequence select="uuid:next-nr() mod 10000"/>
        </xsl:variable>
        <!-- do the math to get the 100 nano second intervals -->
        <xsl:sequence select="
            (days-from-duration($duration-from-1582) * 24 * 60 * 60 +
            hours-from-duration($duration-from-1582) * 60 * 60 +
            minutes-from-duration($duration-from-1582) * 60 +
            seconds-from-duration($duration-from-1582)) * 1000
            * 10000 + $random-offset"/>
    </xsl:function>
    <!-- simple non-generalized function to convert from timestamp to hex -->
    <xsl:function name="uuid:ts-to-hex">
        <xsl:param name="dec-val"/>
        <xsl:value-of separator="" select="
            for $i in 1 to 15
            return (0 to 9, tokenize('A B C D E F', ' '))
            [
            $dec-val idiv
            xs:integer(math:power(16, 15 - $i))
            mod 16 + 1
            ]"/>
    </xsl:function>
    <xsl:function name="math:power">
        <xsl:param name="base"/>
        <xsl:param name="power"/>
        <xsl:choose>
            <xsl:when test="$power &lt; 0 or contains(string($power), '.')">
                <xsl:message terminate="yes">

                    The XSLT template math:power doesn't support negative or

                    fractional arguments.

                </xsl:message>
                <xsl:text>NaN</xsl:text>
            </xsl:when>
            <xsl:otherwise>
                <xsl:call-template name="math:_power">
                    <xsl:with-param name="base" select="$base"/>
                    <xsl:with-param name="power" select="$power"/>
                    <xsl:with-param name="result" select="1"/>
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:function>
    <xsl:template name="math:_power">
        <xsl:param name="base"/>
        <xsl:param name="power"/>
        <xsl:param name="result"/>
        <xsl:choose>
            <xsl:when test="$power = 0">
                <xsl:value-of select="$result"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:call-template name="math:_power">
                    <xsl:with-param name="base" select="$base"/>
                    <xsl:with-param name="power" select="$power - 1"/>
                    <xsl:with-param name="result" select="$result * $base"/>
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>:choose>
    </xsl:template>
</xsl:stylesheet>
Elegance answered 11/6, 2015 at 8:24 Comment(4)
Thanks for sharing. The source (public-contracts-ontology) is a dead link :(Louisalouisburg
It's possible to get 4 different UUID with this code "<xsl:value-of select="concat(' ', uuid:get-uuid()), concat(' ', uuid:get-uuid())" /><xsl:value-of select="concat(' ', uuid:get-uuid()), concat(' ', uuid:get-uuid())" />": 23CD00A6-8952-11E6-2114-09173F13E4C5 23CD00A5-8952-11E6-2114-09173F13E4C5 23CD00A3-8952-11E6-2114-09173F13E4C5 23CD00A4-8952-11E6-2114-09173F13E4C5Louisalouisburg
But using "<xsl:for-each ..." around, it always starts with the same UUID. E.g. "<xsl:for-each select="Test"><xsl:value-of ... /></xsl:for-each>": 382967A6-8952-11E6-2114-09173F13E4C5 382967A5-8952-11E6-2114-09173F13E4C5 382967A3-8952-11E6-2114-09173F13E4C5 382967A4-8952-11E6-2114-09173F13E4C5 382967A6-8952-11E6-2114-09173F13E4C5 382967A5-8952-11E6-2114-09173F13E4C5 382967A3-8952-11E6-2114-09173F13E4C5 382967A4-8952-11E6-2114-09173F13E4C5 Any idea how to get new UUIDs with "xls:for-each"?Louisalouisburg
Use: <xsl:for-each select="uuid:get-uuid()"><xsl:attribute name="Test" select="."/></xsl:for-each> to get unique values in a for-each loopTreen
B
6

Here is the easiest and shortest way to do this in pure XPath 2 or higher:

unparsed-text("https://uuidgen.org/api/v/4")

If you want to generate many GUIDs, say 100, do this:

for $i in 1 to 100
  return unparsed-text(concat("https://uuidgen.org/api/v/4?x=", $i))

Explanation:

Because the standard XSLT 2.0 / XPath 3.0 (and greater version) function unparsed-text() is deterministic it must return the same result on each invocation with the same argument. A compliant XPath implementation must thus cache the first response and only produce the cached response henceforth for the same argument.

To avoid this, we generate a slightly-different URL for each call, thus making the XPath specification authors' efforts futile.

Enjoy enter image description here

Biagio answered 11/11, 2020 at 18:33 Comment(4)
@user:4099593: Please, undelete the other answer and delete this one. Can we at least have a link here to the undeleted answer? This will be helpful for the readers. Thanks for your understandingBiagio
@bhargav-rao, Please, undelete the other answer and delete this one. Can we at least have a link here to the undeleted answer? This will be helpful for the readers. Thanks for your understandingBiagio
noting that uuidgen.org is no longer a good source for uuid generation. API access has been removed and, in turn, html is returned. I had used it for some time previously with great success. Thanks Dimitre for another excellent answer :)Abstractionism
@Abstractionism This is a bad news, but one could get that HTML document and query for $("textarea") or $("textarea[aria-label |= 'UUID']")Biagio
L
4

Since XSLT is a functional language, generating random numbers is not part of the language. That said, there are extension packages (EXSLT) and some processors (Saxon) that support generation of random numbers. If you can't use extensions or Saxon, then I believe you're out of luck.

Longwood answered 14/11, 2011 at 19:48 Comment(8)
How does Saxon support generation of random number?Daff
Saxon comes with the EXSLT random module built-in. See saxonica.com/documentation/extensions/intro.xmlLongwood
Thank you. Too bad the built in EXSLT modules are not available for the Saxon HE (Home Edition).Daff
Not really out of luck... it's just less convenient. Most random number generators are written in software anyway. So if CPU performance is not a bottleneck, you can implement standard random number generation algorithms in pure, standard XSLT.Melville
E.g. Braaksma's timestamp-based implementation (stylusstudio.com/xsllist/200703/post40430.html) uses only one extension function, math:power(). And math:power() is implemented (as a template) using plain XSLT, here: exslt.org/math/functions/power/math.power.template.xslMelville
There is a short XPath expression that produces a GUID: https://mcmap.net/q/716911/-xslt-generate-uuidBiagio
Was that available in 2011?Longwood
@JimGarrison Yes, see: web.archive.org/web/2011*/uuid.org "Saved 16 times between February 20, 2001 and December 13, 2011."Biagio
F
4

Take a look to another question Generate GUID in XSLT.

Probably this article will help you - there defined XSLT functions to generate GUID

Forras answered 14/11, 2011 at 19:57 Comment(0)
M
3

For generating random numbers in XSLT, see Casting the Dice with FXSL: Random Number Generation Functions in XSLT. The only extension function it uses is node-set(), which is no longer necessary in XSLT 2.0.

Also, if the requirement is only that the IDs be unique (not necessarily random), take a look at how to generate unique string. For example if you are generating a UUID for each element of an input XML document, you can use a combination of the URL of the input document, and <xsl:number> to generate a unique string for each element.

Melville answered 14/11, 2011 at 21:22 Comment(0)
A
1

If using .Net's XslCompiledTransform to transform your XSL, you can set the EnableScripts property to true, then use code such as below:

<msxsl:script language="C#" implements-prefix="csharp">
    <![CDATA[
    public static string NewGuid()
    {
        return Guid.NewGuid().ToString();
    }
    ]]>
</msxsl:script>

NB: I've given this custom functionality the name/prefix csharp in the above; but you can call it whatever you like.

For more on enabling scripts, see https://mcmap.net/q/747933/-how-to-enable-xslt-scripting-in-c.

Full XSLT file below to give some additional context:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet 
    version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:csharp="urn:JohnLBevan/NewGuid"
    exclude-result-prefixes="xsl msxsl csharp"
>

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

    <xsl:template match="//*/text()">
        <!-- replaces all text nodes from input document with GUIDs -->
        <xsl:value-of select="csharp:NewGuid()"/>
    </xsl:template>

    <msxsl:script language="C#" implements-prefix="csharp">
        <![CDATA[
        public static string NewGuid()
        {
            return Guid.NewGuid().ToString();
        }
        ]]>
    </msxsl:script>

</xsl:stylesheet>
Abortive answered 26/6, 2017 at 16:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.