Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

xsl: Can a named template return a node list?

Tags:

xslt

xslt-2.0

I am using a recursive template that searches for some specific elements like this:

<xsl:template name="GetProdDependency">
    <xsl:param name="TechProd"></xsl:param>
    <xsl:param name="BeatenPath"></xsl:param>
    <xsl:variable name="TechProdArch" select="$TechProd/pro:own_slot_value[pro:slot_reference='technology_product_architecture']/pro:value"></xsl:variable>
    <xsl:variable name="TechProdArchNode" select="/node()/pro:simple_instance[pro:name=$TechProdArch]"></xsl:variable>
    <xsl:variable name="TechProdCompList" select="$TechProdArchNode/pro:own_slot_value[pro:slot_reference='contained_techProd_components']/pro:value"/>
    <xsl:for-each select="$TechProdCompList">
        <xsl:variable name="TechProdAsRole" select="/node()/pro:simple_instance[pro:name=current()]/pro:own_slot_value[pro:slot_reference='technology_product_as_role']/pro:value"/>
        <xsl:variable name="TechProdRole" select="/node()/pro:simple_instance[pro:name=$TechProdAsRole]/pro:own_slot_value[pro:slot_reference='role_for_technology_provider']/pro:value"/>
        <xsl:variable name="DepTechProd" select="/node()/pro:simple_instance[pro:name=$TechProdRole]"/>
        <!-- Check for beaten Path -->
        <!-- if $DepTechProd/pro:own_slot_value[pro:slot_reference='name']/pro:value in BeatenPath -->
        <xsl:if test="not($BeatenPath[string(.)=string($DepTechProd/pro:own_slot_value[pro:slot_reference='name']/pro:value)])"> 
            <!-- Do the recursion here! -->
            <!--<xsl:value-of select="$DepTechProd/pro:own_slot_value[pro:slot_reference='name']/pro:value"/> (type: <xsl:value-of select="$DepTechProd/pro:type"/> and Class: <xsl:value-of select="$DepTechProd/pro:name"/>)-->
            <!--<xsl:value-of select="$DepTechProd/pro:own_slot_value[pro:slot_reference='name']/pro:value"/>-->
            <xsl:value-of select="$DepTechProd"/>
            <xsl:call-template name="GetProdDependency">
                <xsl:with-param name="TechProd" select="$DepTechProd"></xsl:with-param>
                <xsl:with-param name="BeatenPath" select="$TechProd|$DepTechProd/pro:own_slot_value[pro:slot_reference='name']/pro:value"></xsl:with-param>
                <xsl:with-param name="Rev" select="$Rev + 1"></xsl:with-param>
            </xsl:call-template>
        </xsl:if>
    </xsl:for-each>
</xsl:template> 

This is working fine in the searching etc.

But when I get the result in the original caller, I was expecting to get a list of nodes from the calling.

I call it like:

<xsl:variable name="DelPlist">
<xsl:call-template name="GetProdDependency">
    <xsl:with-param name="TechProd" select="$TechProd"></xsl:with-param>
    <xsl:with-param name="BeatenPath" select="$TechProd/pro:own_slot_value[pro:slot_reference='name']/pro:value"></xsl:with-param>
    <xsl:with-param name="Rev" select="1"></xsl:with-param>
</xsl:call-template>
</xsl:variable>

And I was expecting to get a list of nodes that I can iterate through with <xsl:for-each>. But If I check for the count($DelPlist), I get 1 as result and I can not iterate.

Can somebody help?

like image 248
Kangkan Avatar asked Dec 13 '22 06:12

Kangkan


2 Answers

The answer to your question is: in XSLT 2.0 yes, in XSLT 1.0 no.

In XSLT 2.0 both templates and functions can return any value. The type of the result can be specified using the as attribute (for example as="node()*"), and you can use the xsl:sequence instruction to set the result to be the result of any XPath expression.

In XSLT 1.0, if you capture the result of xsl:call-template in a variable, the value of the variable will always be a result tree fragment.

like image 174
Michael Kay Avatar answered Jan 21 '23 04:01

Michael Kay


You must specify the type of the result of the template in its as attribute.

If unspecified, the type is document-node() and then in order to iterate through the result, you need to get the children of the result.

Solution: Specify the return type of the template with an as attribute.

Here is a complete example:

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/">
     <xsl:variable name="vNodes" as="element()*">
      <xsl:call-template name="genNodes"/>
     </xsl:variable>

     <xsl:for-each select="$vNodes">
      <xsl:value-of select="concat('&#xA;', position(), ': ')"/>
      <xsl:copy-of select="."/>
     </xsl:for-each>
 </xsl:template>

 <xsl:template name="genNodes" as="element()*">
  <a/>
  <b/>
  <c/>
 </xsl:template>
</xsl:stylesheet>

when this transformation is applied on any XML document (not used), the wanted, correct result is produced:

1: <a/>
2: <b/>
3: <c/>
like image 41
Dimitre Novatchev Avatar answered Jan 21 '23 03:01

Dimitre Novatchev