Kewn101
Kewn101

Reputation: 9

XSL Store Value

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

Answers (3)

michael.hor257k
michael.hor257k

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>

Added:

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

Eir&#237;kr &#218;tlendi
Eir&#237;kr &#218;tlendi

Reputation: 1180

Your first template matches, because any well-formed XML input has a root, so / always matches. However:

  1. In best practice, xsl:param is used to bind input parameters, not to define internal variables.
  2. There is no 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.
  3. Your 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.

Update

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

Michael Kay
Michael Kay

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

Related Questions