XSLT to create filtered subset

Discussion in 'XML' started by John Bailo, Jun 12, 2006.

  1. John Bailo

    John Bailo Guest

    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?
    John Bailo, Jun 12, 2006
    #1
    1. Advertising

  2. John Bailo wrote:
    >
    > 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?
    >



    --
    () ASCII Ribbon Campaign | Joe Kesselman
    /\ Stamp out HTML e-mail! | System architexture and kinetic poetry
    Joe Kesselman, Jun 12, 2006
    #2
    1. Advertising

  3. 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.


    --
    () ASCII Ribbon Campaign | Joe Kesselman
    /\ Stamp out HTML e-mail! | System architexture and kinetic poetry
    Joe Kesselman, Jun 12, 2006
    #3
  4. 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.


    --
    () ASCII Ribbon Campaign | Joe Kesselman
    /\ Stamp out HTML e-mail! | System architexture and kinetic poetry
    Joe Kesselman, Jun 12, 2006
    #4
  5. John Bailo

    John Bailo Guest

    Joe Kesselman wrote:

    > 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>
    John Bailo, Jun 12, 2006
    #5
  6. John Bailo

    John Bailo Guest

    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 Bailo wrote:
    > Joe Kesselman wrote:
    >
    >> 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>
    >
    John Bailo, Jun 12, 2006
    #6
  7. 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:apply-templates select="client"/>
    </xsl:for-each>
    > </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.


    --
    () ASCII Ribbon Campaign | Joe Kesselman
    /\ Stamp out HTML e-mail! | System architexture and kinetic poetry
    Joe Kesselman, Jun 12, 2006
    #7
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. vMike
    Replies:
    1
    Views:
    471
    Natty Gur
    Aug 26, 2003
  2. vMike
    Replies:
    1
    Views:
    346
    Natty Gur
    Aug 26, 2003
  3. COMantilles
    Replies:
    0
    Views:
    2,468
    COMantilles
    Sep 13, 2003
  4. Brad
    Replies:
    5
    Views:
    393
    Steven Cheng[MSFT]
    Jan 2, 2004
  5. Replies:
    3
    Views:
    808
Loading...

Share This Page