Search code examples
stringxpathxsltconcatenationsaxon

String-join() function is affected by order of XML structure (Nodes)


I normally use String-join() functions extensively for handling and joining values coming from Database, however currently I observed if the second variable of the function is first fetched/present in the XSL/XML structure than the first variable, then the output is reversed of that expected value. For example:

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

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" exclude-result-prefixes="#all" version="3.0">  
  <xsl:output method="text" encoding="UTF-8"/>  
  <xsl:template match="/"> 
    <xsl:variable name="b"> 
      <row> 
        <value>2</value> 
      </row> 
    </xsl:variable>  
    <xsl:variable name="a"> 
      <row> 
        <value>1</value> 
      </row> 
    </xsl:variable>  
    <xsl:variable name="c"> 
      <row> 
        <value>1</value> 
      </row> 
    </xsl:variable>  
    <xsl:variable name="d"> 
      <row> 
        <value>2</value> 
      </row> 
    </xsl:variable>  
    <!-- Version a-->  
    <xsl:value-of select="string-join(($a,$b)[. != '']/row/value/text(), ';')"/>  
    <!-- OP - 2;1 -->  
    <!-- Version b-->  
    <xsl:value-of select="formatter:crlf()"/>
    <xsl:value-of select="string-join(($c,$d)[. != '']/row/value/text(), ';')"/>
    <!-- OP - 1;2 -->  
    <!-- Version c-->    
    <xsl:value-of select="formatter:crlf()"/>
    <xsl:value-of select="string-join(($a[. != '']/row/value/text(),$b[. != '']/row/value/text()), ';')"/> 
    <!-- OP - 1;2 -->  
  </xsl:template> 
</xsl:stylesheet>

Here, I have four variables a, b, c, d with values 1, 2, 1, 2 respectively and 3 versions of string-join() function. With the first version a I get output 2;1 which is not what I was expecting and it seems since b was declared before a, when fetching the value, b was fetched earlier and in that order it was joined and printed. With version b, I get the expected since c is declared before d. With version c, I did a tweak and was now able to get the correct sequence.

My expectation is that this should NOT happen. In real production scenarios I have many values coming from DBs which are fetched asynchronously and when needed so when I perform string-join() operation I expect the correct output.

I verified this is not due to SAXON or XPATH issue, since I tried both SAXON 10 with XPATH 3 & SAXON 9 with XPATH 2 and was getting the same result.

Let me know what you guys think and what can I do?

  • Note: I cannot change the string-join() function since that brutally affects production environment variables in many different places.

Solution

  • You are using the / operator which sorts nodes in document order so in string-join(($a,$b)[. != '']/row/value/text(), ';'), while you define an order $a,$b, the use of /row/value/text() ensures that document order is used, only that I think document order for nodes from different documents is basically undefined.

    You can use ! instead of / e.g. <xsl:value-of select="string-join(($a,$b)[. != '']!row!value!text(), ';')"/>.

    See example online for Saxon HE 11 and for Saxon HE(C) 12.