xsl-list
[Top] [All Lists]

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

2007-09-05 18:03:13
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[(_at_)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[(_at_)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[(_at_)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[(_at_)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(_at_)CraneSoftwrights(_dot_)com
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


--~------------------------------------------------------------------
XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: <mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--

<Prev in Thread] Current Thread [Next in Thread>