Re: [xsl] xsl with dynamic xpath statement from param and variable

Subject: Re: [xsl] xsl with dynamic xpath statement from param and variable
From: "G. Ken Holman" <gkholman@xxxxxxxxxxxxxxxxxxxx>
Date: Wed, 05 Sep 2007 21:01:33 -0400
At 2007-09-06 11:38 +1200, Ralph Price wrote:
My problem seems like it should be simple

Essentially there are nodes in my xml that I wish to discard based on a
child value existing in that node. That value is to be supplied by a
param.

The original data looks like:
...
---------------------------------------------

I only wish to retain in the output from the xsl the item elements
where, in this case the <dict id="RT.VAL.CANCELDATE"> does not have a
<val> child.

So far the stylesheet looks like:

There are a number of aspects of poor form that you should change.


<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
version="1.0">
        <xsl:output omit-xml-declaration="yes" indent="yes"/>
        <xsl:param name="argDictItem">RT.VAL.CANCELDATE</xsl:param>
        <xsl:variable name="sgl_qte">%27</xsl:variable>

This method of escaping text is not supported in XML. That does not represent a single quote.


<xsl:variable name="xpathDictItem"
select="concat('group/dict[@id=',$sgl_qte,$argDictItem,$sgl_qte,']/val')
"/>

XSLT, as standardized, does not support dynamic evaluation of XPath syntax from a string as you have composed above. Some vendors offer such an extension but extensions are not portable.


        <xsl:template match="/">
        <xsl:comment>
        $xpathDictItem = <xsl:value-of select="$xpathDictItem"/>
        </xsl:comment>
                <xsl:copy>
                        <xsl:apply-templates select="node()"/>
                </xsl:copy>

You don't need to copy the root node, it is implicitly in the result.


        </xsl:template>
        <xsl:template match="node()">

This template rule matches elements, comments, processing instructions and text nodes.


                <xsl:if test="name()!='item'">
                        <!-- carry on copying nodes -->
                        <xsl:copy>
                                <xsl:apply-templates select="node()"/>
                        </xsl:copy>
                </xsl:if>
                <xsl:if test="name()='item'">

The above can be replaced with two template matches.


The use of name()='item' is poor form and when you work with namespaces it will fail.

                        <!-- copy item node only if the argDictItem does
not have a value -->
                        <xsl:choose>
                                <xsl:when test="not($xpathDictItem)">

This does not evaluate the XPath syntax ... this is just checking the string for being empty ... the string is never empty so this is will never be triggered.


                                        <xsl:comment>
                                node discarded - variable value used
                                </xsl:comment>
                                </xsl:when>
                                <xsl:when
test="group/dict[@id='RT.VAL.CANCELDATE']/val">

This is where the change can be made.


                                        <xsl:comment>
                                node discarded - hardcoded value used
                                        </xsl:comment>
                                </xsl:when>
                                <xsl:otherwise>
                                        <xsl:copy>
                                                <xsl:apply-templates
select="node()"/>
                                        </xsl:copy>
                                </xsl:otherwise>
                        </xsl:choose>
                </xsl:if>
        </xsl:template>
</xsl:stylesheet>
...
However I wish to be able to delete the <item> elements based on the
argDictItem parameter rather than my hardcoded value.

In XSLT 1.0 a template rule cannot include a variable reference, while in XSLT 2.0 it can. I've written two solutions since the XSLT 2 is more elegant with the template matches.


And are you sure you wanted to wipe out your source attributes in your target? I assumed this was an oversight, so I'm preserving them. You can easily remove them.

Any leads would be most appreciated.

Two solutions are below. I hope this helps in many ways ... it would seem that you've been misdirected in your XSLT techniques.


. . . . . . . . . Ken


T:\ftemp>type ralph.xml <origenxml xmlns=""> <data> <item> <id>07011*810*00*</id> <obj>RT.VALUATION</obj> <group> <id>MAIN</id> <dict id="RT.VAL.ID.RAW"> <desc>valuation number</desc> <val>07011*810*00*</val> </dict> <dict id="RT.VAL.CANCELDATE"> <desc>cancel date</desc> <val>01 Jan 2006</val> </dict> </group> </item> <item> <id>07011*810*06*</id> <obj>RT.VALUATION</obj> <group> <id>MAIN</id> <dict id="RT.VAL.ID.RAW"> <desc>valuation number</desc> <val>07011*810*06*</val> </dict> <dict id="RT.VAL.CANCELDATE"> <desc>cancel date</desc> </dict> </group> </item> </data> </origenxml>

T:\ftemp>type ralph.xsl
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
                version="1.0">
  <xsl:output omit-xml-declaration="yes" indent="yes"/>
  <xsl:param name="argDictItem">RT.VAL.CANCELDATE</xsl:param>

<xsl:template match="/">
  <xsl:comment>
    $argDictItem = <xsl:value-of select="$argDictItem"/>
  </xsl:comment>
  <xsl:apply-templates select="node()"/>
</xsl:template>

<xsl:template match="*">
  <!--preserve this element-->
  <xsl:copy>
    <xsl:copy-of select="@*"/>
    <xsl:apply-templates select="node()"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="item">
  <!-- copy item node only if the argDictItem does not have a value -->
  <xsl:choose>
    <xsl:when test="group/dict[@id=$argDictItem]/val">
      <xsl:comment>
node discarded - detected <xsl:value-of select="$argDictItem"/> with a val
      </xsl:comment>
    </xsl:when>
    <xsl:otherwise>
      <xsl:copy>
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates
          select="node()"/>
      </xsl:copy>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

</xsl:stylesheet>

T:\ftemp>call xslt ralph.xml ralph.xsl ralph.out

T:\ftemp>type ralph.out
<!--
    $argDictItem = RT.VAL.CANCELDATE--><origenxml>

   <data>
                <!--
node discarded - detected RT.VAL.CANCELDATE with a val
      -->

<item>

<id>07011*810*06*</id>

<obj>RT.VALUATION</obj>

<group>

<id>MAIN</id>

<dict id="RT.VAL.ID.RAW">

<desc>valuation number</desc>

<val>07011*810*06*</val>

</dict>

<dict id="RT.VAL.CANCELDATE">

<desc>cancel date</desc>

</dict>

</group>

</item>

</data>

</origenxml>
T:\ftemp>type ralph2.xsl
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
                version="2.0">
  <xsl:output omit-xml-declaration="yes" indent="yes"/>
  <xsl:param name="argDictItem">RT.VAL.CANCELDATE</xsl:param>

<xsl:template match="/">
  <xsl:comment>
    $argDictItem = <xsl:value-of select="$argDictItem"/>
  </xsl:comment>
  <xsl:apply-templates select="node()"/>
</xsl:template>

<xsl:template match="*">
  <!--preserve this element-->
  <xsl:copy>
    <xsl:copy-of select="@*"/>
    <xsl:apply-templates select="node()"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="item[group/dict[@id=$argDictItem]/val]">
  <xsl:comment>
node discarded - detected <xsl:value-of select="$argDictItem"/> with a val
  </xsl:comment>
</xsl:template>

</xsl:stylesheet>
T:\ftemp>call xslt2 ralph.xml ralph2.xsl ralph2.out
<!--
$argDictItem = RT.VAL.CANCELDATE--><origenxml>
<data>
<!--
node discarded - detected RT.VAL.CANCELDATE with a val
-->
<item>
<id>07011*810*06*</id>
<obj>RT.VALUATION</obj>
<group>
<id>MAIN</id>
<dict id="RT.VAL.ID.RAW">
<desc>valuation number</desc>
<val>07011*810*06*</val>
</dict>
<dict id="RT.VAL.CANCELDATE">
<desc>cancel date</desc>
</dict>
</group>
</item>
</data>
</origenxml>



-- Upcoming public training: XSLT/XSL-FO Sep 10, UBL/code lists Oct 1 World-wide corporate, govt. & user group XML, XSL and UBL training RSS feeds: publicly-available developer resources and training G. Ken Holman mailto:gkholman@xxxxxxxxxxxxxxxxxxxx Crane Softwrights Ltd. http://www.CraneSoftwrights.com/s/ Box 266, Kars, Ontario CANADA K0A-2E0 +1(613)489-0999 (F:-0995) Male Cancer Awareness Jul'07 http://www.CraneSoftwrights.com/s/bc Legal business disclaimers: http://www.CraneSoftwrights.com/legal

Current Thread