Reputation: 9
I would like to store variable from XML input. I will then use this value to test against another element value. The XML is generated by IBM Watson Explorer. Here's the XML (Updated):
<vce>
<param name="v:project" value="pru-collection"/>
<param name="query" value="xyz"/>
<param name="render.function" value="xml-feed-display"/>
<param name="content-type" value="text/xml"/>
<list path="" num="23" level="0" start="0" per="25">
<document url="https://example.net">
<content name="Avatar_Image" type="text">...</content>
<content name="keywords" type="text">xyz</content>
<content name="owner" type="text">...</content>
</document>
<document url="https://example.net">
<content name="Avatar_Image" type="text">...</content>
<content name="keywords" type="text">123</content>
<content name="owner" type="text">...</content>
</document>
<document url="https://example.net">
<content name="Avatar_Image" type="text">...</content>
<content name="keywords" type="text">abc</content>
<content name="owner" type="text">...</content>
</document>
</list>
</vce>
and here is my XSL. This is a free text XSL stylesheet that is internal to IBM application:
<xsl:template match="/">
<xsl:param name="saveQuery" select="**../param[@name='query']/@value**" />
<scope>
<xsl:copy-of select="/scope/*[not(name() = 'document')]" />
<boost name="df" />
<xsl:apply-templates />
</scope>
</xsl:template>
<xsl:template match="document">
<document url="{@url}">
<xsl:if test="viv:test(content[@name='keywords'], '$saveQuery', 'case-insensitive-regex')">
<xsl:attribute name="boost-name">df</xsl:attribute>
<xsl:attribute name="boost-display">boost-and-list</xsl:attribute>
</xsl:if>
<xsl:copy-of select="@*" />
<xsl:copy-of select="* | text() | comment()" />
</document>
</xsl:template>
It looks like $saveQuery
is not correctly storing the value of the 'query' tag. For testing purposes, by manually comparing the content[@name='keywords'
with actual value xyz
instead of $saveQuery
, it works fine.
Updated Code
<xsl:template match="/vce">
<scope>
<xsl:copy-of select="/scope/*[not(name() = 'document')]" />
<boost name="df" />
<xsl:apply-templates />
<xsl:apply-templates>
<xsl:with-param name="saveQuery" select="param[@name='query']/@value" />
</xsl:apply-templates>
</scope>
</xsl:template>
<xsl:template match="*">
<xsl:param name="saveQuery" />
<xsl:apply-templates>
<xsl:with-param name="saveQuery" select="$saveQuery" />
</xsl:apply-templates>
</xsl:template>
<xsl:template match="document">
<xsl:param name="saveQuery" />
<document url="{@url}">
<xsl:if test="viv:test(content[@name='keywords'], '$saveQuery', 'case-insensitive-regex')">
<xsl:attribute name="boost-name">df</xsl:attribute>
<xsl:attribute name="boost-display">boost-and-list</xsl:attribute>
</xsl:if>
<xsl:copy-of select="@*" />
<xsl:copy-of select="* | text() | comment()" />
</document>
</xsl:template>
Updated 2.0 This code works fine in xsltransform tool but not on the IBM environment. Any idea folks why the value is still not being passed to saveQuery? Or maybe idea about different approach?
<xsl:template match="/vce">
<scope>
<xsl:apply-templates select="list/document">
<xsl:with-param name="saveQuery" select="'xyz'" />
</xsl:apply-templates>
</scope>
</xsl:template>
<xsl:template match="document">
<!-- for chk this line works -->
<!-- <xsl:param name="saveQuery" select="'xyz'"/>-->
<xsl:param name="saveQuery" />
<document>
<xsl:if test="content[@name='keywords'] = $saveQuery">
<xsl:attribute name="boost-name">df</xsl:attribute>
<xsl:attribute name="boost-display">boost-and-list</xsl:attribute>
</xsl:if>
<xsl:copy-of select="@*" />
<xsl:copy-of select="* | text() | comment()" />
</document>
</xsl:template>
Upvotes: 0
Views: 327
Reputation: 117073
I am guessing you want to do something like:
<xsl:template match="/vce">
<scope>
<!-- some content here -->
<xsl:apply-templates select="list/document">
<xsl:with-param name="saveQuery" select="param[@name='query']/@value"/>
</xsl:apply-templates>
</scope>
</xsl:template>
<xsl:template match="document">
<xsl:param name="saveQuery"/>
<document>
<!-- use the parameter here -->
</document>
</xsl:template>
Note that the xsl:apply-templates
explicitly selects the target document
elements, so the parameter value is passed directly to the matching template. Without this, you would have to add your own template to replace the built-in template that ensures recursion, and make it carry the parameter as it traverses the tree:
<xsl:template match="/vce">
<scope>
<!-- some content here -->
<xsl:apply-templates>
<xsl:with-param name="saveQuery" select="param[@name='query']/@value"/>
</xsl:apply-templates>
</scope>
</xsl:template>
<xsl:template match="*">
<xsl:param name="saveQuery"/>
<xsl:apply-templates>
<xsl:with-param name="saveQuery" select="$saveQuery"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="document">
<xsl:param name="saveQuery"/>
<document>
<!-- use the parameter here -->
</document>
</xsl:template>
Apparently your processor is non-conforming. You may try accessing the node directly from the target template, e.g. something like:
<xsl:template match="/vce">
<scope>
<!-- some content here -->
<xsl:apply-templates select="list/document"/>
</scope>
</xsl:template>
<xsl:template match="document">
<xsl:variable name="saveQuery" select="/vce/param[@name='query']/@value"/>
<document>
<!-- use the variable here -->
</document>
</xsl:template>
Or even more directly as:
<xsl:template match="document">
<document>
<xsl:if test="content[@name='keywords']=/vce/param[@name='query']/@value">
<xsl:attribute name="boost-name">df</xsl:attribute>
<xsl:attribute name="boost-display">boost-and-list</xsl:attribute>
</xsl:if>
<xsl:copy-of select="@*|node()" />
</document>
</xsl:template>
But with a non-conforming processor it's a pure guess what might work or not.
Upvotes: 0
Reputation: 1180
Your first template matches, because any well-formed XML input has a root, so /
always matches. However:
xsl:param
is used to bind input parameters, not to define internal variables.query
element anywhere in your input XML, let alone at the start of the file, so the select="/query"
statement results in an empty set.scope
code is likewise connected to nothing in your input XML.Your second template will never match, because your input XML has no document
element. It thus follows that there is also no url
attribute for this missing document
element.
With your recent edit to expand what we see of your input XML, document
in your second template could now match. However, your first template matches on /
(the logical root), and applying templates from there will only pass along anything at the top level -- in your case, the vce
element. Since vce
has no matching template, none of the vce
children have templates applied, and since document
is a grandchild of vce
, that template will never match.
scope
and query
are still nowhere to be found, so these portions of your first template will continue to fail.
Upvotes: 0
Reputation: 163458
To do this kind of thing, you need to pass the parameters from one template to another. In the calling template:
<xsl:apply-templates select="document">
<xsl:with-param name="saveQuery" select="./query"/>
</xsl:apply-templates>
In the called template:
<xsl:template match="document">
<xsl:param name="query"/>
...
</xsl:template>
Variables in XSLT have static scope - when you refer to a variable or parameter using $var
, the variable var
must be declared either in a global declaration, or earlier in the same template or function.
Upvotes: 1