Using xslt3 (Saxon JS) and not bound to a specific XSLT version
Given this sample.xml
<?xml version=”1.0″ encoding=”UTF-8″?>
<tasks>
<task id="id0"/>
<task id="id1" parent="#id0" deps="#id2 #id3 #id0"/>
<task id="id2" parent="#id0"/>
<task id="id3" parent="#id0"/>
</tasks>
and this sample.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="/tasks/task">
<xsl:variable name="parentTask" select="replace(@parent,'#','')"/>
<xsl:variable name="depTasks">
<xsl:choose>
<xsl:when test="@deps">
<xsl:value-of select="tokenize(replace(@deps,'#',''),'\s')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$parentTask"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<parent id="{@id}">
<xsl:for-each select="/tasks/task[@id=$depTasks]">
<dep>
<xsl:value-of select="@id"/>
</dep>
</xsl:for-each>
</parent>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
I would expect the $depTasks
variable to contain an array (all the deps listed in the deps
attribute) or a single value (the dep listed in the parent
attribute), and obtain a transformation like this:
<?xml version="1.0" encoding="UTF-8"?>
<parent id="id0"/>
<parent id="id1">
<dep>id2</dep>
<dep>id3</dep>
<dep>id0</dep>
</parent>
<parent id="id2">
<dep>id0</dep>
</parent>
<parent id="id3">
<dep>id0</dep>
</parent>
BUT instead I'm getting this result (it seems that only the first element of the tokenization is assigned)
<?xml version="1.0" encoding="UTF-8"?>
<parent id="id0"/>
<parent id="id1">
<dep>id2</dep>
<!-- MISSING DEPS -->
</parent>
<parent id="id2">
<dep>id0</dep>
</parent>
<parent id="id3">
<dep>id0</dep>
</parent>
Do problems or limitations exist for variable assigment inside conditions? Or am I missing something here?
Note that, if I change a bit the stylesheet and declare the variable OUTSIDE a condition, an array is assigned as I expect.
sample2.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="/tasks/task">
<xsl:variable name="parentTask" select="replace(@parent,'#','')"/>
<!-- VARIABLE assignement outside a condition: value is an array -->
<xsl:variable name="depTasks" select="tokenize(replace(@deps,'#',''),'\s')"/>
<parent id="{@id}">
<xsl:for-each select="/tasks/task[@id=$depTasks]">
<dep>
<xsl:value-of select="@id"/>
</dep>
</xsl:for-each>
</parent>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
resulting in this transformation
<?xml version="1.0" encoding="UTF-8"?>
<parent id="id0"/>
<parent id="id1">
<dep>id0</dep>
<dep>id2</dep>
<dep>id3</dep>
</parent>
<parent id="id2"/>
<parent id="id3"/>
Your are not using any arrays in your code at all. In
<xsl:variable name="depTasks">
<xsl:choose>
<xsl:when test="@deps">
<xsl:value-of select="tokenize(replace(@deps,'#',''),'\s')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$parentTask"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
you are creating a result tree fragment containing a text node with the string value of the tokenize
call or the $parentTask
variable.
In <xsl:variable name="depTasks" select="tokenize(replace(@deps,'#',''),'\s')"/>
the value of the variable is the result of the tokenize
call which is a sequence of string values.
If for the previous approach, although not recommeded, you wanted a sequence of string, you would need to use
<xsl:variable name="depTasks" as="xs:string*">
...
<xsl:sequence select="tokenize(...)"/>
...
</xsl:variable>
The preferred way for the more complex part would be select="if (@deps) then tokenize(...) else $parentTask"
.