XSL Invalid Type Error - Setting Variable using Va

2019-07-22 08:06发布

问题:

I am trying to get value-of $deal/total_price. In the first block, I am able to get value and everything works great. In the second block where I am using value-of to set the variable named deal, I get an error when trying to display $deal/total_price. How can I return $deal/total_price using setup in second block?

Works:

<xsl:variable name="deal" select="/webpage/results/cars/*[partner_name = $company_name and vehicle_class_description = $vehicle_class_description_full]" />        
<xsl:value-of select="$deal/total_price"/>

Does Not Work :

<xsl:variable name="deal">
    <xsl:value-of select="/webpage/results/cars/*[partner_name = $company_name and vehicle_class_description = $vehicle_class_description_full]"/>
</xsl:variable>
<xsl:value-of select="$deal/total_price"/>

I am receiving the following errors/warnings:

Warning: XSLTProcessor::transformToXml(): Invalid type
Warning: XSLTProcessor::transformToXml(): runtime error:
Warning: XSLTProcessor::transformToXml(): XPath evaluation returned no result

回答1:

With <xsl:variable name="deal" select="/webpage/results/cars/*[partner_name = $company_name and vehicle_class_description = $vehicle_class_description_full]" /> the variable type is defined by evaluating the select XPath expression which returns a node-set in XSLT/XPath 1.0. You can then do XPath navigation on the node-set, such as selecting child nodes.

With

<xsl:variable name="deal">
    <xsl:value-of select="/webpage/results/cars/*[partner_name = $company_name and vehicle_class_description = $vehicle_class_description_full]"/>
</xsl:variable>

the variable type is a result tree fragment containing a text node with the string value of the first node selected by the inner value-of. With a variable of type result tree fragment you can't do any XPath navigation, you can output its string value using value-of or its tree fragment using copy-of. If you want to do XPath navigation then you first need to use exsl:node-set or similar to convert the result tree fragment into a node-set, but even if you do that for your second sample you would get with exsl:node-set($deal) a node-set with a document node containing a text node. Thus if you want to have a variable containing nodes in XSLT 1.0 you need to use

<xsl:variable name="deal-rtf">
  <foo>
    <bar>...</bar>
  </foo>
</xsl:variable>
<xsl:variable name="deal" select="exsl:node-set($deal-rtf)" xmlns:exsl="http://exslt.org/common"/>

<xsl:value-of select="$deal/foo/bar"/>

Some XSLT 1.0 processors (notably the various MSXML versions as used by IE or Edge and XslTransform in the .NET framework) do not support exsl:node-set but rather a similar function in a proprietary namespace (i.e. <xsl:variable name="deal" select="ms:node-set($deal-rtf)" xmlns:ms="urn:schemas-microsoft-com:xslt"/>).

Inside of an xsl:variable you can of course use xsl:choose, e.g.

<xsl:variable name="deal-rtf">
  <xsl:choose>
     <xsl:when test="...">
       <xsl:copy-of select="/webpage/results/cars/*[partner_name = $company_name and vehicle_class_description = $vehicle_class_description_full and pay_now = 'Y']"/>
     </xsl:when>
     <xsl:otherwise>
        <xsl:copy-of select="webpage/results/cars/*[partner_name = $company_name and vehicle_class_description = $vehicle_class_description_full]"/>
     </xsl:otherwise>
  </xsl:choose>
</xsl:variable>

<xsl:variable name="deal" select="exsl:node-set($deal-rtf)" xmlns:exsl="http://exslt.org/common"/>

<xsl:value-of select="$deal/total_price"/>


回答2:

The problem is that the second version returns a RTF(Resulting Tree Fragment) and not a node-set like the first version. An XPath query cannot be applied to a RTF, it can only be applied to a node-set.

An explanation of the difference can be found here at Oracle.

In XSLT-1.0 you cannot avoid that, like it is explained here at StackOverflow.

I quote from the Oracle link:

A result tree fragment is equivalent to a node-set that contains just the root node.
You cannot apply operators like "/", "//" or predicate on a result tree fragments. They are only applicable for node-set datatypes.

The (probably easiest) solution would be using XSLT-2.0, because in XSLT-2.0 all variables are node-sets and RTFs have been extinguished.