Search code examples
xslt

how to generate unique string


I want to transform an XML document into HTML. Some XML elements have links to others documents like:

<link href="1.html">

In the HTML output, I want to get:

<a href="1.html&no_cache={unique_id}">

How can I generate this unique fairly large ID?


Solution

  • To start with, I assume that due to some unknown reason you cannot use the absolute URL in the link as the required UID -- this is the simplest and most natural solution.

    In case my assumption is correct, then:

    This is an easy task for XSLT.

    Because the OP wants the generated ids to be the same when the transformation is performed several times, it isn't appropriate to use the generate-id() function.

    Here is one simple way of producing stable ids:

    <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:template match="node()|@*">
         <xsl:copy>
           <xsl:apply-templates select="node()|@*"/>
         </xsl:copy>
     </xsl:template>
    
     <xsl:template match="link[@href]">
      <xsl:variable name="vUid">
        <xsl:number level="any" count="link[@href]"/>
      </xsl:variable>
       <a href="{@href}&amp;no_cache={{{$vUid}}}"/>
     </xsl:template>
    </xsl:stylesheet>
    

    when this transformation is applied on the following XML document (regardless how many times):

    <t>
     <link href="1.html"/>
     <a>
       <link href="2.html"/>
      <b>
        <link href="3.html"/>
        <c>
         <link href="4.html"/>
        </c>
        <link href="5.html"/>
      </b>
      <link href="6.html"/>
      <d>
       <link href="7.html"/>
      </d>
     </a>
     <link href="8.html"/>
     <e>
      <link href="9.html"/>
     </e>
     <link href="10.html"/>
    </t>
    

    the wanted, same, correct result is produced every time:

    <t>
       <a href="1.html&amp;no_cache={1}"/>
       <a>
          <a href="2.html&amp;no_cache={2}"/>
          <b>
             <a href="3.html&amp;no_cache={3}"/>
             <c>
                <a href="4.html&amp;no_cache={4}"/>
             </c>
             <a href="5.html&amp;no_cache={5}"/>
          </b>
          <a href="6.html&amp;no_cache={6}"/>
          <d>
             <a href="7.html&amp;no_cache={7}"/>
          </d>
       </a>
       <a href="8.html&amp;no_cache={8}"/>
       <e>
          <a href="9.html&amp;no_cache={9}"/>
       </e>
       <a href="10.html&amp;no_cache={10}"/>
    </t>
    

    Do note: The use of <xsl:number> to produce the id.

    If the same link can occur several times in the document and we need all occurences to use the same id, here is the solution for this problem:

    <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="kHrefByVal" match="link/@href" use="."/>
    
     <xsl:variable name="vUniqHrefs" select=
      "//link/@href
           [generate-id()
           =
            generate-id(key('kHrefByVal',.)[1])
           ]
      "/>
    
    
     <xsl:template match="node()|@*">
         <xsl:copy>
           <xsl:apply-templates select="node()|@*"/>
         </xsl:copy>
     </xsl:template>
    
     <xsl:template match="link[@href]">
      <xsl:variable name="vthisHref" select="@href"/>
    
      <xsl:variable name="vUid">
       <xsl:for-each select="$vUniqHrefs">
        <xsl:if test=". = $vthisHref">
         <xsl:value-of select="position()"/>
        </xsl:if>
       </xsl:for-each>
      </xsl:variable>
       <a href="{@href}&amp;no_cache={{{$vUid}}}"/>
     </xsl:template>
    </xsl:stylesheet>
    

    when this transformation is applied on the following XML document:

    <t>
     <link href="1.html"/>
     <a>
       <link href="2.html"/>
      <b>
        <link href="1.html"/>
        <c>
         <link href="3.html"/>
        </c>
        <link href="2.html"/>
      </b>
      <link href="1.html"/>
      <d>
       <link href="3.html"/>
      </d>
     </a>
     <link href="4.html"/>
     <e>
      <link href="2.html"/>
     </e>
     <link href="4.html"/>
    </t>
    

    the wanted, correct result is produced:

    <t>
       <a href="1.html&amp;no_cache={1}"/>
       <a>
          <a href="2.html&amp;no_cache={2}"/>
          <b>
             <a href="1.html&amp;no_cache={1}"/>
             <c>
                <a href="3.html&amp;no_cache={3}"/>
             </c>
             <a href="2.html&amp;no_cache={2}"/>
          </b>
          <a href="1.html&amp;no_cache={1}"/>
          <d>
             <a href="3.html&amp;no_cache={3}"/>
          </d>
       </a>
       <a href="4.html&amp;no_cache={4}"/>
       <e>
          <a href="2.html&amp;no_cache={2}"/>
       </e>
       <a href="4.html&amp;no_cache={4}"/>
    </t>