removal of empty nodes resulting from application of other templates

Discussion in 'XML' started by tschwartz@revasystems.com, Feb 13, 2007.

  1. Guest

    I'm trying to write a stylesheet which removes nodes which are empty
    as a result of other template processing.

    For example, given the following xml, I'd like to:
    - remove all "b" elements
    - remove all "a" elements which, as a result of "b" element
    removal, now have no children

    so, starting with:

    <?xml version="1.0" encoding="UTF-8"?>
    <doc>
    <a>
    <b/>
    </a>
    <a>
    <b/>
    <c/>
    </a>
    </doc>

    I'd like to end up with:

    <?xml version="1.0" encoding="UTF-8"?>
    <doc>
    <a>
    <c/>
    </a>
    </doc>

    I've written this stylesheet:

    <xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:eek:utput omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <!-- matches all nodes and attributes -->
    <xsl:template match="node()|@*" name="identity">
    <xsl:copy>
    <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
    </xsl:template>

    <!-- rule for element "a", ought to reproduce "a" only if it has
    children -->
    <xsl:template match="//a">
    <xsl:if test="count(./*) &gt; 0">
    <xsl:call-template name="identity"/>
    </xsl:if>
    </xsl:template>

    <!-- remove all b nodes -->
    <xsl:template match="//b" priority="3" name="b-removal"/>

    </xsl:stylesheet>

    but the resultant output has not removed the empty "a" element:
    <doc>
    <a/>
    <a>
    <c/>
    </a>
    </doc>

    Can anyone suggest a way to do this?

    Thanks,

    Ted
    , Feb 13, 2007
    #1
    1. Advertising

  2. Guest

    On 13 Feb., 23:57, wrote:
    > I'm trying to write a stylesheet which removes nodes which are empty
    > as a result of other template processing.


    How about applying that stylesheet to your data twice?

    Regards,
    Johannes
    , Feb 14, 2007
    #2
    1. Advertising

  3. wrote:
    >>I'm trying to write a stylesheet which removes nodes which are empty
    >>as a result of other template processing.


    XSLT 1.0 doesn't have any built-in ability to do two-pass processing.
    The usual solution is to run two stylesheets in succession, or the same
    one twice.

    If you MUST do it in a single stylesheet, the usual workaround is to use
    the EXSLT node-set function. Do a first pass outputting into a variable
    to obtain a Result Tree Fragment, then use exslt:node-set() to make that
    available to XSLT in a form that it can walk over again, generating the
    final output. Modes may be useful in keeping the two passes separate, if
    the behavior is different in one than in the other.

    (XSLT 2.0 -- still not widely supported, unfortunately, since it adds a
    lot of complexity along with the simplifications -- eliminates the
    distinction between node-sets and RTFs, and can do what I've just
    described without needing a "standard nonstandard" helper.)

    --
    Joe Kesselman / Beware the fury of a patient man. -- John Dryden
    Joseph Kesselman, Feb 14, 2007
    #3
  4. Guest

    On Feb 13, 9:32 pm, Joseph Kesselman <>
    wrote:
    > wrote:
    > >>I'm trying to write a stylesheet which removes nodes which are empty
    > >>as a result of other template processing.

    >
    > XSLT 1.0 doesn't have any built-in ability to do two-pass processing.
    > The usual solution is to run two stylesheets in succession, or the same
    > one twice.
    >
    > If you MUST do it in a single stylesheet, the usual workaround is to use
    > the EXSLT node-set function. Do a first pass outputting into a variable
    > to obtain a Result Tree Fragment, then use exslt:node-set() to make that
    > available to XSLT in a form that it can walk over again, generating the
    > final output. Modes may be useful in keeping the two passes separate, if
    > the behavior is different in one than in the other.
    >
    > (XSLT 2.0 -- still not widely supported, unfortunately, since it adds a
    > lot of complexity along with the simplifications -- eliminates the
    > distinction between node-sets and RTFs, and can do what I've just
    > described without needing a "standard nonstandard" helper.)
    >
    > --
    > Joe Kesselman / Beware the fury of a patient man. -- John Dryden


    Thanks for the help! As I would like to apply the templates in one
    pass I've successfully used the approach of using included
    stylesheets and modes described above. Here are the stylesheets:


    first-pass.xsl:

    <xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:exslt="http://exslt.org/common">
    <xsl:eek:utput omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <!-- matches all nodes and attributes -->
    <xsl:template match="node()|@*" name="identity" mode="first-pass">
    <xsl:copy>
    <xsl:apply-templates select="node()|@*" mode="first-pass"/>
    </xsl:copy>
    </xsl:template>

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

    <!-- remove all b nodes -->
    <xsl:template match="//b" priority="3" name="b-removal" mode="first-
    pass"/>

    </xsl:stylesheet>

    second-pass.xsl:

    <xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:exslt="http://exslt.org/common">
    <xsl:eek:utput omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <!-- matches all nodes and attributes -->
    <xsl:template match="node()|@*" name="identity" mode="second-pass">
    <xsl:copy>
    <xsl:apply-templates select="node()|@*" mode="second-pass"/>
    </xsl:copy>
    </xsl:template>

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

    <!-- rule for element "a", ought to reproduce a only if it has
    children -->
    <xsl:template match="//a" mode="second-pass">
    <xsl:if test="count(./*) &gt; 0">
    <xsl:call-template name="identity"/>
    </xsl:if>
    </xsl:template>

    </xsl:stylesheet>

    main.xsl:
    <xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:exslt="http://exslt.org/common">
    <xsl:eek:utput omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:include href="first-pass.xsl"/>
    <xsl:include href="second-pass.xsl"/>

    <xsl:template match="/">
    <xsl:variable name="first">
    <xsl:apply-templates mode="first-pass" />
    </xsl:variable>
    <xsl:apply-templates select="exslt:node-set($first)" mode="second-
    pass" />
    </xsl:template>

    </xsl:stylesheet>


    Then with execution of main.xsl against the source xml, I get the
    desired output:

    <doc>
    <a>
    <c/>
    </a>
    </doc>

    Thanks again.
    , Feb 15, 2007
    #4
    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. gavnosis
    Replies:
    0
    Views:
    519
    gavnosis
    Aug 2, 2003
  2. Timo Nentwig

    selecting nodes between other nodes

    Timo Nentwig, Jun 16, 2004, in forum: XML
    Replies:
    1
    Views:
    402
    Patrick TJ McPhee
    Jun 17, 2004
  3. Replies:
    2
    Views:
    394
  4. JKop
    Replies:
    3
    Views:
    468
  5. recover
    Replies:
    2
    Views:
    800
    recover
    Jul 25, 2006
Loading...

Share This Page