XSLT to create filtered subset

J

John Bailo

Given this XML:

<?xml version="1.0" encoding="UTF-8"?>
<pallet>
<position row="0" bay="0" level="A">
<client id="ABC"></client>
</position>
<position row="1" bay="1" level="B">
<client id="DEF"></client>
</position>
<position row="1" bay="1" level="C">
<client id="GHI"></client>
</position>
</pallet>

I want to return a subset,

<pallet>
<position row="0" bay="0" level="A">
<client id="ABC"></client>
</position>
</pallet>


But I can't seem to extend my stylesheet to something 3 nodes deep.

This:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:eek:utput method="xml"/>
<xsl:template match="/">
<pallet>
<xsl:apply-templates select="pallet/position"/>
</pallet>
</xsl:template>
<xsl:template match="pallet">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="position[@row=0]">
<position>
<xsl:attribute name="row">
<xsl:value-of select="@row"/>
</xsl:attribute>
<xsl:attribute name="bay">
<xsl:value-of select="@bay"/>
</xsl:attribute>
<xsl:attribute name="level">
<xsl:value-of select="@level"/>
</xsl:attribute>
</position>
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>

will return

<?xml version="1.0" encoding="UTF-16"?>
<pallet>
<position row="0" bay="0" level="A" />
</pallet>


But say I want to extend it to return:

<?xml version="1.0" encoding="UTF-16"?>
<pallet>
<position row="0" bay="0" level="A" />
<client id="ABC">
</client>
</pallet>

How do I do that?
 
J

Joe Kesselman

John said:
Given this XML:

<?xml version="1.0" encoding="UTF-8"?>
<pallet>
<position row="0" bay="0" level="A">
<client id="ABC"></client>
</position>
<position row="1" bay="1" level="B">
<client id="DEF"></client>
</position>
<position row="1" bay="1" level="C">
<client id="GHI"></client>
</position>
</pallet>

I want to return a subset,

<pallet>
<position row="0" bay="0" level="A">
<client id="ABC"></client>
</position>
</pallet>


But I can't seem to extend my stylesheet to something 3 nodes deep.

This:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:eek:utput method="xml"/>
<xsl:template match="/">
<pallet>
<xsl:apply-templates select="pallet/position"/>
</pallet>
</xsl:template>
<xsl:template match="pallet">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="position[@row=0]">
<position>
<xsl:attribute name="row">
<xsl:value-of select="@row"/>
</xsl:attribute>
<xsl:attribute name="bay">
<xsl:value-of select="@bay"/>
</xsl:attribute>
<xsl:attribute name="level">
<xsl:value-of select="@level"/>
</xsl:attribute>
</position>
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>

will return

<?xml version="1.0" encoding="UTF-16"?>
<pallet>
<position row="0" bay="0" level="A" />
</pallet>


But say I want to extend it to return:

<?xml version="1.0" encoding="UTF-16"?>
<pallet>
<position row="0" bay="0" level="A" />
<client id="ABC">
</client>
</pallet>

How do I do that?
 
J

Joe Kesselman

The complicating factor here is that you want to pull <client> out of
<position>. That means you need to independently match on "the client
which is within the position with row='0'".

Maintaining your somewhat verbose style:

<xsl:template match="client[../position[@row='0']"
<client>
<xsl:attribute name="id">
<xsl:value-of select="@id"/>
</xsl:attribute>
<!-- possible apply-templates here? -->
</client>
</xsl:template/>

If you haven't already done so, you really want to look at the xsl:copy
and xsl:copy-of directives, and at the standard "identity transformation
with exceptions" approachh; those techniques would make this partcular
example significantly cleaner, unless you have a Good Reason for doing
everything explicitly.
 
J

Joe Kesselman

Sorry, forgot to say that you also need to modify:

<xsl:template match="pallet">
<xsl:apply-templates select="position"/>
<xsl:apply-templates select="position/client"/>
</xsl:template>

Actually, performance would probably be better if you changed that to

<xsl:template match="pallet">
<xsl:apply-templates select="position[@row=0]"/>
<xsl:apply-templates select="position[@row=0]/client"/>
</xsl:template>

You could then consider removing the predicates from the templates for
position and client.
 
J

John Bailo

Joe said:
You could then consider removing the predicates from the templates for
position and client.

I am still having trouble. I changed the xslt to this. I think it
should produce an exact replica of the original XML:

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

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

<xsl:template match="pallet">
<!-- BEGIN YOUR CHANGE -->
<xsl:apply-templates select="position"/>
<xsl:apply-templates select="position/client"/>
<!-- END YOUR CHANGE -->
<xsl:apply-templates/>
</xsl:template>

<xsl:template match="position">
<position>
<xsl:attribute name="row">
<xsl:value-of select="@row"/>
</xsl:attribute>
<xsl:attribute name="bay">
<xsl:value-of select="@bay"/>
</xsl:attribute>
<xsl:attribute name="level">
<xsl:value-of select="@level"/>
</xsl:attribute>
</position>
<xsl:apply-templates/>
</xsl:template>

<!-- BEGIN YOUR CHANGE -->
<xsl:template match="client[../position]">
<client>
<xsl:attribute name="id">
<xsl:value-of select="@id"/>
</xsl:attribute>
<!-- possible apply-templates here? -->
</client>
</xsl:template>
<!-- END YOUR CHANGE -->

</xsl:stylesheet>


And the result is:

<?xml version="1.0" encoding="UTF-16"?>
<pallet>
<position row="0" bay="0" level="A" />
<position row="1" bay="1" level="B" />
<position row="1" bay="1" level="C" />
</pallet>


Instead of

<?xml version="1.0" encoding="UTF-8"?>
<pallet>
<position row="0" bay="0" level="A">
<client id="ABC"></client>
</position>
<position row="1" bay="1" level="B">
<client id="DEF"></client>
</position>
<position row="1" bay="1" level="C">
<client id="GHI"></client>
</position>
</pallet>
 
J

John Bailo

Ok, I think I got it -- thanks for all your help!
You're second suggestion gave me the clues.

I also will try your optimizations -- you've opened my eyes as far as
the potential elegance of this language.


Here's my stylesheet:

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

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

<xsl:template match="pallet">
<xsl:apply-templates select="position"/>
<xsl:apply-templates select="position/client"/>

</xsl:template>

<xsl:template match="position">
<position>
<xsl:attribute name="row">
<xsl:value-of select="@row"/>
</xsl:attribute>
<xsl:attribute name="bay">
<xsl:value-of select="@bay"/>
</xsl:attribute>
<xsl:attribute name="level">
<xsl:value-of select="@level"/>
</xsl:attribute>
<xsl:apply-templates select="client"/>
</position>

</xsl:template>


<xsl:template match="client">
<client>
<xsl:attribute name="id">
<xsl:value-of select="@id"/>
</xsl:attribute>
<!-- possible apply-templates here? -->
</client>

</xsl:template>



</xsl:stylesheet>



John said:
Joe said:
You could then consider removing the predicates from the templates for
position and client.


I am still having trouble. I changed the xslt to this. I think it
should produce an exact replica of the original XML:

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

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

<xsl:template match="pallet">
<!-- BEGIN YOUR CHANGE -->
<xsl:apply-templates select="position"/>
<xsl:apply-templates select="position/client"/>
<!-- END YOUR CHANGE -->
<xsl:apply-templates/>
</xsl:template>

<xsl:template match="position">
<position>
<xsl:attribute name="row">
<xsl:value-of select="@row"/>
</xsl:attribute>
<xsl:attribute name="bay">
<xsl:value-of select="@bay"/>
</xsl:attribute>
<xsl:attribute name="level">
<xsl:value-of select="@level"/>
</xsl:attribute>
</position>
<xsl:apply-templates/>
</xsl:template>

<!-- BEGIN YOUR CHANGE -->
<xsl:template match="client[../position]">
<client>
<xsl:attribute name="id">
<xsl:value-of select="@id"/>
</xsl:attribute>
<!-- possible apply-templates here? -->
</client>
</xsl:template>
<!-- END YOUR CHANGE -->

</xsl:stylesheet>


And the result is:

<?xml version="1.0" encoding="UTF-16"?>
<pallet>
<position row="0" bay="0" level="A" />
<position row="1" bay="1" level="B" />
<position row="1" bay="1" level="C" />
</pallet>


Instead of

<?xml version="1.0" encoding="UTF-8"?>
<pallet>
<position row="0" bay="0" level="A">
<client id="ABC"></client>
</position>
<position row="1" bay="1" level="B">
<client id="DEF"></client>
</position>
<position row="1" bay="1" level="C">
<client id="GHI"></client>
</position>
</pallet>
 
J

Joe Kesselman

John Bailo wrote:

Actually, let's make that cleaner in several ways
<xsl:template match="pallet">
<xsl:for-each select="position[@row='0']">
<xsl:apply-templates select="."/>
</xsl:template> ....
<xsl:template match="position"> ....
<xsl:template match="client">

Let pallet handle selecting which descendents you care about, then just
provide templates for how to render them once selected.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,011
Latest member
AjaUqq1950

Latest Threads

Top