Assigning parameter value to the xsl: for each - xslt

Can anybody who has worked with XSLT help me on this?
I am using XSL version 1.0.
I have declared a parameter in XSL file like:
<xsl:param name="HDISageHelpPath"/>
Now I am assigning the value to this parameter from an asp page . The value which I assign is "document('../ChannelData/Sage/help/ic/xml/HDI.xml')/HelpFiles/Help". Now I want to assign this parameter to the <xsl for each> like
<xsl:for-each select="msxsl:node-set($HDISageHelpPath)" > (This does not work)
But it does not work. I checked the parameter value by debugging it as below
<debug tree="$HDISageHelpPath">
<xsl:copy-of select="$HDISageHelpPath"/>
</debug>
I'm able to print the value and it seems correct. In fact when I assign the static path ("document('../ChannelData/Sage/help/ic/xml/HDI.xml')/HelpFiles/Help") by hard-coding it, it works
<xsl:for-each select="document('../ChannelData/Sage/help/ic/xml/HDI.xml')/HelpFiles/Help"> (This works)
Can anyone please let me know why assigning the parameter to xsl:for-each does not work?
Note: I have referred the site "http://www.dpawson.co.uk/xsl/sect2/N1553.html"

You can't easily evaluate dynamic strings as XPath expressions in XSLT 1.0. They must be hard-coded, normally.
There's EXSLT's dyn:evaluate(), but I doubt you can use that with the MXSML processor.
As an alternative approach, you could either try passing the file path only:
<xsl:param name="HDISageHelpFilePath"/>
<!-- ... -->
<xsl:for-each select="document($HDISageHelpFilePath)/HelpFiles/Help">
</xsl:for-each>
or making placeholder, replacing it with search-and-replace before you load the actual XSL code into the processor (as a string). This is a bit messy and error-prone, but it could give you the possibility to use an actual dynamic XPath expression.
<xsl:for-each select="%HELP_FILE_XPATH%">
</xsl:for-each>
Load the file as text, replace %HELP_FILE_XPATH% with your actual XPath, feed it to the processor. If it loads, you are fine, if it doesn't, your input XPath was malformed.

Related

XSL getting out of context using dynamic XPATH

I'm trying to reformat an XML I get from an appliance into an HTML table, and it's format is not usual.
It use unique references in node name's, like this:
/network/content/host/content/REF_1/content
/network/content/network/content/REF_2/content
and then, it use the same references to another part of the file, as a value of a content node, like this:
/rules/content/rules/content/REF_3/content/sources/content/name = REF_1
/rules/content/rules/content/REF_3/content/destinations/content/name = REF_2
I'm trying to write a template for content that instead of getting me REF_ID which is unique, I try to get the name, in the other branch leaf. this mean I'm trying to find a value that is out of my actual context.
I'm able to retrieve the name XPATH using this variable:
<xsl:variable name='objName' select="concat('/storage/objects/',#linkclass,'/content/',#linktype,'/content/',current(),'/content/name/content')" />
but, I'm not able to use this XPATH in a query like:
<xsl:value-of select="{$objName}">
I suppose this doesn't work because it's out of context but when I ask statically for one of those XPATH I get the value.
My full code is not very complicated:
<xsl:template match="content">
<xsl:variable name='objXPATH' select="concat('/storage/objects/',#linkclass,'/content/',#linktype,'/content/',current(),'/content/name/content')" />
<xsl:variable name='obj' select="{$objXPATH}" />
<xsl:element name="a">
<xsl:attribute name="href">
#<xsl:value-of select="."/>
</xsl:attribute>
<xsl:value-of select="$obj"/>
<br />
</xsl:element>
</xsl:template>
I need help to fix this, I'm on it since one day with no evolution, and it's driving me crazy. I'm more like a script kiddie than a real developer.
Dynamic evaluation (treating a string in a variable as an XPath expression and evaluating it) is available as a vendor extension in a number of XSLT processors, and it becomes part of the standard with the introduction of xsl:evaluate in XSLT 3.0. If your XSLT processor doesn't have such an extension you may be able to write it yourself. Alternatively, if you explain the problem better, we may be able to suggest a solution that does not require dynamic evaluation.

Saxon error "XPTY0019: Required item type of first operand of '/' is node(); supplied value has item type xs:string"

I have an XSL program which in turn generates an XSL program, which depending on the input might look like this:
<xsl:variable name="patterns"/> <!--empty in this particular case-->
<xsl:template name="token">
<xsl:for-each select="$patterns/pattern">
...
When I then run the generated stylesheet, Saxon, bless its heart, is apparently doing some kind of static analysis and complains:
XPTY0019: Required item type of first operand of '/' is node(); supplied value has item type xs:string
and won't even compile the stylesheet.
My workaround was to generate a dummy element in the $patterns nodeset, but is there any cleaner approach here, or way to suppress the compile error?
According to http://www.w3.org/TR/xslt20/#variable-values, "If the variable-binding element has empty content and has neither a select attribute nor an as attribute, then the supplied value of the variable is a zero-length string.".
So you need to change that, for instance by doing <xsl:variable name="patterns" select="()"/> to bind an empty sequence as the variable value.
In XSLT 1.0 (the same would work also with XSLT 2.0) use:
<xsl:variable name="patterns" select="/.."/>
This provides to the XSLT processor the information, necessary to conclude that the type of the $patterns variable is node-set.

How to use an XSL variable as a condition to evaluate XPath?

I have faced an issue when using a variable as a condition for XPath evaluation. I have the following template which works fine:
<xsl:template name="typeReasonDic">
<xsl:variable name="dic" select="$schema//xs:simpleType[#name = 'type_reason_et']"/>
<!-- do something with the variable -->
</xsl:template>
However, when I change it to look like this:
<xsl:template name="typeReasonDic">
<xsl:param name="choose_dic" select="#name = 'type_reason_et'"/>
<xsl:variable name="dic" select="$schema//xs:simpleType[$choose_dic]"/>
<!-- do something with the variable -->
</xsl:template>
it fails to find the desired node.
What I wish to get is a template with a default value for $choose_dic which can be overriden where necessary.
What am I missing here?
UPD: there is this link I found with the description of what I'm trying to do, but it doesn't seem to work for me.
You can't do this directly in XSLT 1.0 or 2.0 without an extension function. The problem is that with
<xsl:template name="typeReasonDic">
<xsl:param name="choose_dic" select="#name = 'type_reason_et'"/>
<xsl:variable name="dic" select="$schema//xs:simpleType[$choose_dic]"/>
<!-- do something with the variable -->
</xsl:template>
the <xsl:param> will evaluate its select expression a single time in the current context and store the true/false result of this evaluation in the $choose_dic variable. The <xsl:variable> will therefore select either all xs:simpleType elements under the $schema (if $choose_dic is true) or none of them (if $choose_dic) is false. This is very different from
<xsl:variable name="dic" select="$schema//xs:simpleType[#name = 'type_reason_et']"/>
which will evaluate #name = 'type_reason_et' repeatedly, in the context of each xsl:simpleType, and select those elements for which the expression evaluated to true.
If you store the XPath expression as a string you can use an extension function such as dyn:evaluate or the XSLT 3.0 xsl:evaluate element if you're using Saxon.
By doing
<xsl:param name="choose_dic" select="#name = 'type_reason_et'"/>
the XSL engine will try to evaluate "#name = 'type_reason_et'" as an XPath expression, and will assign the RESULT to your variable.
You should use the following variable declaration instead:
<xsl:param name="choose_dic">#name = 'type_reason_et'</xsl:param>
This is the default value, but you can override it when you call your template by using xsl:with-param.
XSLT is not a macro language where you might be able to concatenate your code at run-time from strings and then evaluate them dynamically. So in general for your purpose you would need an extension function to evaluate an XPath expression stored in a string or you need to look into a new XSLT 3.0 features like http://www.saxonica.com/documentation/xsl-elements/evaluate.xml.
What is possible in the scope of XSLT 1.0 or 2.0 is doing e.g.
<xsl:param name="p1" select="'foo'"/>
<xsl:variable name="v1" select="//bar[#att = $p1]"/>
where the param holds a value you compare to other value, for instance those in a node like an attribute or element node.

XSLT to call secondary XML based on first XML element/attribute

love the stuff - newbie Æthelred here
I have a XSLT 1.0 file pulling in a secondary XML (to a variable) to build a table
<xsl:variable name="table_values" select="document('./table_variants/external_table.xml')/xml/channel_1"/>
I then get the values i need from the variable, eg:
<xsl:value-of select="$table_values/monkey/tennis/#medals"/>
<xsl:value-of select="$table_values/monkey/tennis/#bananas"/>
What i want to do is have the first XML trigger/steer where to look for the table data.
I hoped i could, within the triggered XML, state the last part of the xpath - the 'channel_1' or 'channel_2',
<xsl:value-of select="xml/external_table_channel_to_use"/>
but apparently i cannot create a xpath on the fly like that
Please - What can i do?
What i want to do is have the first XML trigger/steer where to look
for the table data. I hoped i could, within the triggered XML, state
the last part of the xpath - the 'channel_1' or 'channel_2',
<xsl:value-of select="xml/external_table_channel_to_use"/> but
apparently i cannot create a xpath on the fly like that
Please - What can i do?
This can easily be done just extending the code that you already have.
Change this:
<xsl:variable name="table_values" select=
"document('./table_variants/external_table.xml')/xml/channel_1"/>
to this:
<xsl:variable name="table_values" select=
"document('./table_variants/external_table.xml')
/xml/*[name() = $channelName"/>
Needless to say, the variable (or global, external param) $channelName should have a value that is the (string) name of the element you want to use in the last location step of the XPath expression.

Using dynamic xpath in XSLT

I am using a variable say xpathvar in the XSLT whose value will be supplied at the time of call to XSLT. How can i achieve this?
My XSLT file looks like -
<xsl:param name="xpathvar"/>
<xsl:param name="keyxpath"/>
<xsl:template match="/">
<listofResults>
<xsl:for-each select="$xpathvar">
<keyvalues><xsl:value-of select="xalan:evaluate(substring-before($keyxpath,'||'))"/></keyvalues>
<keyvalues><xsl:value-of select="xalan:evaluate(substring-after($keyxpath,'||'))"/></keyvalues>
</xsl:for-each>
</listofResults>
</xsl:template>
</xsl:stylesheet>
If I mention the variable in the for-each, it throws error. Please guide how can I achieve this.
Thanks!
According to Global xsl:param containing xpath expression string it can't be done purely with plain old XSLT, you need to use the evaluate extension see: Xalan evaluate expression
I dont think this can be done like this you must use xpath so example would be
<template match="Node[name=$xpathvar]" />
<xsl:for-each select="*[name()=$xpathvar]">
</xsl:for-each>
I think it does not let you use the variable directly because it is not a nodeset.
If I mention the variable in the
for-each, it throws error
In XSLT 1.0 (it looks you are using Xalan), you can iterate over node sets, only. So $xpathvar should be an instance of node set data type.
In XSLT 2.0 you can iterate over sequence (including scalar values).
Also, if the string containing the "dynamic" XPath expression is simple enough (only QName test step, maybe positional predicates) this could be done (as is already answered in SO) with standar XSLT.