Re: [xsl] passing filtered tree to template

Subject: Re: [xsl] passing filtered tree to template
From: Stefan Hunziker <stefan@xxxxxxxxxxxxx>
Date: Thu, 9 Jun 2011 17:45:01 +0200
Hi Brandon

thank you for your proposition. This approach looks quite good for me,
I will try to implement it next week and give you a feedback. That
XSLT 2 (or even 3) would give me easier and cleaner solutions is quite
clear, hope Microsoft will notice that once (I'm forced to use
.net..)!

kind regards
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