1

I am using xslt with Saxon HE and I used the method specified here to generate UUIDs but I am getting the same id for all nodes of the same level.

What I am getting now:

<aaa uid="6EE63184-6950-11E8-9859-09173F13E4C5">
      <discount uid="6EE63183-6950-11E8-9859-09173F13E4C5">
         <li id="#d2e17">Dark Coffee<a href="#d2e20">USD 1.8</a>
         </li>
      </discount>
   </aaa>
   <aaa uid="6EE63184-6950-11E8-9859-09173F13E4C5">
      <discount uid="6EE63183-6950-11E8-9859-09173F13E4C5">
         <li id="#d2e32">Milk Shake<a href="#d2e35">USD 2.6</a>
         </li>
      </discount>
   </aaa>
   <aaa uid="6EE63184-6950-11E8-9859-09173F13E4C5">
      <discount uid="6EE63183-6950-11E8-9859-09173F13E4C5">
         <li id="#d2e47">Iced Coffee<a href="#d2e50">USD 1.5</a>
         </li>
      </discount>
   </aaa>
   <aaa uid="6EE63184-6950-11E8-9859-09173F13E4C5">
      <discount uid="6EE63183-6950-11E8-9859-09173F13E4C5">
         <li id="#d2e62">Bottled Water<a href="#d2e65">USD 2.5</a>
         </li>
      </discount>
   </aaa>

I want the ids of each aaa tag and discount tag to be unique from each other. what change should I do to achieve this.

Below is the xslt I used.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://www.w3.org/2005/02/xpath-functions" xmlns:m0="http://services.samples" xmlns:math="http://exslt.org/math" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:uuid="http://www.uuid.org" version="2.0" exclude-result-prefixes="m0 fn">
  <xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
  <xsl:param name="drink_name"/>
  <xsl:template match="/">
     <Payment xmlns="http://ws.apache.org/ns/synapse">
        <xsl:for-each select="//Order/lunch">
           <xsl:element name="aaa">
              <xsl:attribute name="uid" select="uuid:get-uuid()"/>
              <discount>
                 <xsl:attribute name="uid" select="uuid:get-uuid()"/>
                 <li id="#{generate-id(drinkName)}">
                    <xsl:value-of select="drinkName"/>
                    <a href="#{generate-id(drinkPrice)}">
                       <xsl:value-of select="drinkPrice"/>
                    </a>
                 </li>
              </discount>
           </xsl:element>
        </xsl:for-each>
     </Payment>
  </xsl:template>
  <!-- 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 doesnt 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>

Michael Kay
  • 156,231
  • 11
  • 92
  • 164
Asma Zinneera Jabir
  • 801
  • 3
  • 13
  • 31

1 Answers1

0

(Preliminary incomplete answer, I will come back to this later)

This whole stylesheet depends very strongly on the notion that when a function creates new nodes, then it will create different nodes each time it is called, and different nodes will have different results for generate-id(). That is, it's relying on the fact that calling a function has a subtle side-effect, with the consequence that it's possible to call the same function twice and get different results. This of course is a departure from the normal rules of functional programming, and it has nasty consequences for optimization, because it means that function calls can't be pulled out of loops (if you call f(3) within a loop, you can't pull that call out of the loop and only do it once).

This is discussed in detail at https://www.w3.org/TR/xslt-30/#function-determinism

XSLT 3.0 allows you to declare the function's expectations using the attribute new-each-time="yes". This is supposed to be the default, but because Saxon has a history of attempting optimization in the absence of strict rules in this area, it's probably trying to make its own assessment of whether multiple calls are necessary.

Using the -explain option when you run the stylesheet gives you feedback on what optimizations Saxon is applying. You can also switch off selected optimizations, for example -opt:-fl switches off loop-lifting and function inlining.

A much better approach to the whole problem would be to use the new fn:random-number-generator() function. Unfortunately this uses higher order functions so it is not available in Saxon-HE.

Michael Kay
  • 156,231
  • 11
  • 92
  • 164