Re: [xsl] passing filtered tree to template

Subject: Re: [xsl] passing filtered tree to template
From: Stefan Hunziker <stefan@xxxxxxxxxxxxx>
Date: Thu, 16 Jun 2011 11:34:27 +0200
Hi Brandon

I have implemented your approach and it works perfectly

Thank you very much
Stefan

On Thu, Jun 9, 2011 at 2:17 PM, Brandon Ibach
<brandon.ibach@xxxxxxxxxxxxxxxxxxx> wrote:
> Here's an approach that works while keeping the "criteria" for which
> items to include in the caller.  No guarantees about how the
> performance of this approach might scale.  Features in XSLT 3.0 (or
> even XSLT 2.0, I suspect) would make this cleaner and probably perform
> better.
>
> The xsl:for-each inside your "items" template basically turns it into
> an "item" template, so I cleaned it up a bit by just making the
> template match "item" and moving the test to determine of the item is
> the first in its group into an xsl:if.  I also made the code more
> concise by using attribute value templates, but this is purely a
> matter of taste, rather than functionality.
>
> Your "property = 'x'" filter also needs to be applied to any results
> of the key() function.  I did this below, while keeping the filter out
> of the "item" template, by passing in the filtered set of items and
> checking for group membership using the same basic technique you used
> for node identity when checking to see if an item is the first in its
> group.
>
> <xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
>    <xsl:template match="test">
>        <myTest>
>            <xsl:variable name="items" select="items/item[property = 'x']"/>
>            <xsl:apply-templates select="$items">
>                <xsl:sort select="group"/>
>                <xsl:with-param name="items" select="$items"/>
>            </xsl:apply-templates>
>        </myTest>
>    </xsl:template>
>
>    <xsl:key name="groupKey" match="item" use="group"/>
>    <xsl:template match="item">
>        <xsl:param name="items" select="/.."/>
>        <xsl:if test="count(. | key('groupKey', group)[count(. |
> $items) = count($items)][1]) = 1">
>            <myGroup Name="{group}"
> Sum="{sum(key('groupKey',group)[count(. | $items) =
> count($items)]/amount)}"/>
>        </xsl:if>
>    </xsl:template>
> </xsl:stylesheet>
>
> Hope this helps.
>
> -Brandon :)
>
>
> On Tue, Jun 7, 2011 at 2:34 AM, Stefan Hunziker <stefan@xxxxxxxxxxxxx>
wrote:
>> Hi Abel
>>
>> thank you for your answer. Sorry when my instructions were confusing.
>> I give it another try, in fact the items template is a little bit more
>> complicated because I want to apply a grouping mechanism.
>>
>> given the following xml:
>>
>> <test>
>>        <items>
>>                <item>
>>                        <index>1</index>
>>                        <group>b</group>
>>                        <property>x</property>
>>                        <amount>1</amount>
>>                </item>
>>                <item>
>>                        <index>2</index>
>>                        <group>a</group>
>>                        <property>y</property>
>>                        <amount>3</amount>
>>                </item>
>>                <item>
>>                        <index>3</index>
>>                        <group>a</group>
>>                        <property>x</property>
>>                        <amount>6</amount>
>>                </item>
>>        </items>
>> </test>
>>
>> I want to process this stylesheet:
>>
>> <xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
>>        <xsl:template match="test">
>>                <myTest>
>>                <xsl:variable name="xItems">
>>                        <xsl:copy-of select="items"/>
>>                </xsl:variable>
>>                        <xsl:apply-templates
select="items[item/property='y']"/>
>>                </myTest>
>>        </xsl:template>
>>
>>  <xsl:key name="groupKey" match="item" use="group"/>
>>  <xsl:template match="items">
>>    <xsl:for-each select="item[count(.| key('groupKey', group)[1]) = 1]">
>>      <xsl:sort select="group"/>
>>      <myGroup>
>>        <xsl:attribute name="Name">
>>          <xsl:value-of select="group"/>
>>        </xsl:attribute>
>>        <xsl:attribute name="Sum">
>>          <xsl:value-of select="sum(key('groupKey',group)/amount)"/>
>>        </xsl:attribute>
>>      </myGroup>
>>    </xsl:for-each>
>>        </xsl:template>
>> </xsl:stylesheet>
>>
>>
>> So the target is to have a group list with the corresponding sum of
>> amount, so I don't have a "item" template, but only an "items". But
>> therefore only the property=x items should be processed. Is that more
>> clear?
>>
>> kind regards
>> Stefan
>>
>>
>>
>> On Mon, Jun 6, 2011 at 6:11 PM, Abel Braaksma <abel.online@xxxxxxxxx>
wrote:
>>> Hi Stefan,
>>>
>>> The easy way out here is to add the filter to the matching template
>>> instruction, and add another matching template for those that don't
match,
>>> or, if all "item" elements need to be processed in the same way, but item
>>> with property=x need to be processed with an extra instruction, something
>>> like this should work:
>>>
>>> <xsl:template match="items">
>>>    <xsl:apply-templates select="item" />
>>> </xsl:template>
>>>
>>> <!-- called ONLY when property X is true -->
>>> <xsl:template match="item[property='x']">
>>>    <myItem>
>>>        <xsl:value-of select="index"/>
>>>    </myItem>
>>> </xsl:template>
>>>
>>> <!-- called for all other situations (may leave empty if you don't want
to
>>> process them) -->
>>> <xsl:template match="item">
>>>    <otherItem>
>>>        <xsl:value-of select="index"/>
>>>    </otherItem>
>>> </xsl:template>
>>>
>>> I have to note that I found your instruction a tad confusing, so if I
>>> misunderstood, please try to clarify with a simplified input/output
example
>>> of XML.
>>>
>>>
>>> Kind regards,
>>>
>>> Abel Braaksma
>>>
>>>
>>>
>>> On 6-6-2011 17:56, Stefan Hunziker wrote:
>>>>
>>>> Hi
>>>> I have the following problem: Given an xml like the following:
>>>>
>>>> <test>
>>>>     <items>
>>>>         <item>
>>>>             <index>1</index>
>>>>             <property>x</property>
>>>>         </item>
>>>>         <item>
>>>>             <index>2</index>
>>>>             <property>y</property>
>>>>         </item>
>>>>         <item>
>>>>             <index>3</index>
>>>>             <property>x</property>
>>>>         </item>
>>>>     </items>
>>>> </test>
>>>>
>>>>
>>>> and a stylesheet like:
>>>>
>>>>
>>>> <xsl:stylesheet version="1.0"
>>>> xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
>>>>     <xsl:template match="test">
>>>>         <myTest>
>>>>         <xsl:variable name="xItems">
>>>>             <xsl:copy-of select="items"/>
>>>>         </xsl:variable>
>>>>             <xsl:apply-templates
>>>> select="items[item/property='y']"/><!-- wrong try! -->
>>>>         </myTest>
>>>>     </xsl:template>
>>>>
>>>>     <xsl:template match="items">
>>>>         <xsl:for-each select="item">
>>>>             <myItem>
>>>>                 <xsl:value-of select="index"/>
>>>>             </myItem>
>>>>         </xsl:for-each>
>>>>     </xsl:template>
>>>> </xsl:stylesheet>
>>>>
>>>>
>>>>
>>>> I would like to pass only the items with property='x' to the items
>>>> matching template. The first try as written above of course doesn't
>>>> work, if there is an x item then the whole tree is passed, if there is
>>>> none, nothing is passed. I do further processing in the items
>>>> template, so I don't want to change it to match="item" or to apply the
>>>> filter in the items template.
>>>>
>>>> Any help is much appreciated!
>>>>
>>>> thanks, kind regards
>>>>
>>>> Stefan
>
>



--
****************************
stefan hunziker
kanalweg 10
ch-3315 bdtterkinden

tel +41 32 665 17 76
mob +41 79 455 30 62

email stefan@xxxxxxxxxxxxx

Current Thread