[xsl] Re: filtering problem

Subject: [xsl] Re: filtering problem
From: Adrian Herscu <adrian.herscu@xxxxxxxxx>
Date: Sat, 19 Jun 2010 21:12:02 +0300
Hi Michael,

thanks for the help :)

I tried to apply your idea on my template (written in XSL 1.0 + Xalan Extensions); here is what I got:

The input xml:
<?xml version="1.0" encoding="UTF-8"?>
<procedure name="RunSomething">
  <command src="10" />
  <command src="11" />
  <command src="12" />
  <command src="13" />
  <call name="DoFoo">
    <command src="20" />
    <command src="21" />
    <command src="22" />
    <call name="MakeFoo">
      <command src="30" />
      <command src="31" failed="true()" />
      <command src="32" />
    </call>
    <command src="24" />
    <command src="25" failed="true()" />
    <call name="WriteFoo">
      <command src="40" />
      <command src="41" failed="true()" />
      <command src="42" />
    </call>
    <command src="26" />
  </call>
</procedure>

The XSL:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"; xmlns:xalan="http://xml.apache.org/xalan";
exclude-result-prefixes="xalan">


  <xsl:output method="xml" indent="yes" encoding="UTF-8"
    media-type="text/xml" xalan:indent-amount="2" />

<xsl:strip-space elements="*" />

<xsl:variable name="firstFailure" select="(//command[@failed=true()])[1]" />
<xsl:variable name="pathToFirstFailure"
select="xalan:nodeset($firstFailure)/ancestor-or-self::*" />


  <xsl:template match="command[@failed=true()]">
    <failing-command src="{@src}" />
  </xsl:template>

<xsl:template
match="*[xalan:intersection(node(), xalan:nodeset($pathToFirstFailure))]">
<xsl:copy>
<xsl:copy-of select="@*" />
<xsl:apply-templates />
</xsl:copy>
</xsl:template>


<xsl:template match="*" />

</xsl:stylesheet>

The output:
<?xml version="1.0" encoding="UTF-8"?>
<procedure name="RunSomething">
  <call name="DoFoo">
    <call name="MakeFoo">
      <failing-command src="31" />
    </call>
    <failing-command src="25" />
  </call>
</procedure>

My problem is with the <failing-command src="25" /> element; it should not be generated. How do I filter it out?

Thanks,
Adrian.

On 6/17/2010 12:15 PM, Michael Kay wrote:

Perhaps something like this:


<xsl:variable name="firstFailure"
select="(//command[@failed='true()])[1]"/>

<xsl:variable name="pathToFirstFailure"
select="$firstFailure/ancestor-or-self::*"/>

<xsl:template match="*[. intersect $pathToFirstFailure]">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>

<xsl:template match="*"/>

You don't see this coding pattern very often, but I sometimes find it
quite useful: select the "interesting" nodes in a global variable, and
then use this global variable in a match pattern to steer which template
rule is used to process which nodes. In XSLT 2.1 you will be able to
replace the match pattern above with

match="$pathToFirstFailure"

Michael Kay
Saxonica

On 17/06/2010 09:54, Adrian Herscu wrote:
Hi all,

Consider the following input XML file:

<?xml version="1.0" encoding="UTF-8"?>
<batch name="level1">
<command src="1" />
<command src="2" failed="true()" />
<command src="3" />
<command src="4" failed="true()" />
<batch name="level2">
<command src="10" />
<command src="11" failed="true()" />
<command src="12" />
<batch name="level3">
<command src="20" />
<command src="21" failed="true()" />
<command src="22" />
</batch>
<command src="30" />
<command src="31" failed="true()" />
<command src="32" />
</batch>
</batch>

I need an XSL transformation that scans the top-level batch until it
finds a command marked as failed, no matter at which internal
batch-level it is, preserving the batch level on the report.

Currently, I have this XSL:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
xmlns:xalan="http://xml.apache.org/xalan";>

<xsl:output method="xml" indent="yes" encoding="UTF-8"
media-type="text/xml" xalan:indent-amount="2" />

<xsl:strip-space elements="*" />

<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>

<!--
FIXME: batches are matched even though there is a prior failed
command or batch containing a failed command
-->
<xsl:template match="batch">
<batch name="{@name}">
<xsl:apply-templates />
</batch>
</xsl:template>

<!-- match only the first failing command -->
<xsl:template match="command[@failed=true()][1]">
<failed-command src="{@src}" />
</xsl:template>
</xsl:stylesheet>

which generates:

<?xml version="1.0" encoding="UTF-8"?>
<batch xmlns:xalan="http://xml.apache.org/xalan"; name="level1">
<failed-command src="2" />
<batch name="level2">
<failed-command src="11" />
<batch name="level3">
<failed-command src="21" />
</batch>
</batch>
</batch>

instead of:

<?xml version="1.0" encoding="UTF-8"?>
<batch xmlns:xalan="http://xml.apache.org/xalan"; name="level1">
<failed-command src="2" />
</batch>

Another example -- if only command[@src='21'] is marked failed, then
the generated output should be:

<?xml version="1.0" encoding="UTF-8"?>
<batch name="level1">
<batch name="level2">
<batch name="level3">
<failed-command src="21" />
</batch>
</batch>
</batch>

Thanks for your help,
Adrian.

Current Thread