Remove parent element if there are no children

Discussion in 'XML' started by CI, Apr 13, 2007.

  1. CI

    CI Guest

    I have the following XML file

    <Book>
    <Chapters>
    <Chapter number="1"/>
    <Chapter number="2"/>
    </Chapters>
    <Colors>
    <Color RGB="255"/>
    <Color RGB="211" name="Red"/>
    <Color name="Yellow"/>
    <Color RGB="127"/>
    </Colors>
    </Book>

    I want to get a tree with the <Color> elements that don't have RGB
    attribute:

    So I came up with this XSLT sheet.

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


    <xsl:template match="@*|node()">
    <xsl:copy>
    <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
    </xsl:template>


    <xsl:template match="Color">

    <xsl:choose>
    <xsl:when test="@RGB">

    </xsl:when>
    <xsl:eek:therwise>
    <xsl:copy-of select="."/>
    </xsl:eek:therwise>
    </xsl:choose>

    </xsl:template>

    </xsl:stylesheet>

    It works correctly, but if in my XML file all the <Color> elements
    have RGB attribute the resulted tree
    contains empty <Colors></Colors> node.

    Anyone had an idea how to conditionally remove it?

    Regards,

    Michael
     
    CI, Apr 13, 2007
    #1
    1. Advertisements

  2. Anyone had an idea how to conditionally remove it?

    Adding a check for no-children-exist to your RGB-attribute-exists test
    ought to do it, right?
     
    Joseph Kesselman, Apr 13, 2007
    #2
    1. Advertisements

  3. Don't think about "removing", think about "not copying".

    You want to copy the <Colors> element only if it contains something
    other than a <Color> element with an RGB attribute.

    Complication: Removing the <Color> elements from the above example does
    NOT make <Colors> empty, even if you remove Yellow -- because it still
    contains the whitespace text nodes between the elements.

    It's possible to solve this in a single pass, but I think it's going to
    be ugly. You may want to think about making it simpler by running a
    second stylesheet that removes <Colors> elements that contain only
    whitespace... or, if you're willing to rely on the nodeset extensions,
    running two passes within a single stylesheet by first styling part or
    all of the document into a variable and then styling from the variable
    to your actual output. Look for examples of use of the exslt:node-set
    extension for information about how to do this.
     
    Joseph Kesselman, Apr 14, 2007
    #3
  4. CI

    CI Guest

    Thanks Joe, I appreciate it.

    I posted this problem to another group and Dimitre Novatchev
    suggested the following 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="*"/>

    <xsl:template match="node()|@*">
    <xsl:copy>
    <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
    </xsl:template>

    <xsl:template match="Colors[not(Color[not(@RGB)])]"/>
    <xsl:template match="Color[@RGB]"/>
    </xsl:stylesheet>
    ------------------------------------------------------------------------------



    Regards,

    Michael
     
    CI, Apr 14, 2007
    #4
  5. OK, that will certainly discard <Colors> elements that do not contain a
    <Color> which does not have the RGB attribute. If the only things that
    will be in <Colors> is <Color> elements and whitespace, that should
    work. If there's anything else which might appear there, you may wind up
    deleting some elements that you wanted to keep.
     
    Joe Kesselman, Apr 14, 2007
    #5
  6. Of course, and this is exactly what the OP wanted! :eek:)


    Cheers,
    Dimitre Novatchev
     
    Dimitre Novatchev, Apr 14, 2007
    #6
  7. CI

    p.lepin Guest

    This is a very clean and neat solution,--which is hardly
    surprising,--but I always cringe a bit when I have to
    express the same thing twice in the code. The following
    looks a bit scarier, but has what counts as virtue with me:
    it's re-uses the condition that both your rules are
    dependent upon:

    <xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="@*|node()">
    <xsl:copy>
    <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
    </xsl:template>
    <xsl:template match="Colors">
    <xsl:variable name="color-elements"
    select="Color[not(@RGB)]"/>
    <xsl:if test="$color-elements">
    <xsl:copy>
    <xsl:apply-templates select="$color-elements"/>
    </xsl:copy>
    </xsl:if>
    </xsl:template>
    </xsl:stylesheet>

    YMMV.
     
    p.lepin, Apr 14, 2007
    #7
  8. CI

    p.lepin Guest

    On a side note, XSLT2 is bliss:

    <xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
    <xsl:variable name="post-pass-1">
    <xsl:apply-templates mode="pass-1"/>
    </xsl:variable>
    <xsl:apply-templates select="$post-pass-1"
    mode="pass-2"/>
    </xsl:template>
    <xsl:template match="@*|node()" mode="pass-1">
    <xsl:copy>
    <xsl:apply-templates select="@*|node()"
    mode="pass-1"/>
    </xsl:copy>
    </xsl:template>
    <xsl:template match="Color[@RGB]" mode="pass-1"/>
    <xsl:template match="@*|node()" mode="pass-2">
    <xsl:copy>
    <xsl:apply-templates select="@*|node()"
    mode="pass-2"/>
    </xsl:copy>
    </xsl:template>
    <xsl:template match="Colors[not(*)]" mode="pass-2"/>
    </xsl:stylesheet>
     
    p.lepin, Apr 14, 2007
    #8
  9. A minor change, but a good one: XSLT removed the distinction between
    Result Tree Fragments and Node Sets (both are now grouped together as
    Temporary Trees), so one doesn't need the node-set extension to convert
    the former into the latter.

    Outside of that, this is the same two-past solution I suggested.
     
    Joe Kesselman, Apr 14, 2007
    #9
  10. Granted. But I tend to practice defensive programming, so I look for the
    maximally robust solution...
     
    Joe Kesselman, Apr 14, 2007
    #10

  11. C'mon people,

    Do you know how much additional memory such a solution may use? Twice the
    memory of one-pass solution.

    Where possible, do try to "fuse" (the term is "fusion" or "deforestation") a
    pipeline of passes into a single pass.

    And, of course, I am not rediscovering the wheel here...


    Cheers,
    Dimitre Novatchev.
     
    Dimitre Novatchev, Apr 14, 2007
    #11
  12. It may, or it may not, depending on the internal data model of the
    processor. And for simple input documents, that really may not matter.

    In fact, what I'd do if taking this approach was to do two-pass
    processing only locally, on the element which requires it -- so the
    overhead is both smaller and transient.

    It can be taken further... but see first paragraph; before spending too
    much effort on optimization, consider whether the improvement is worth
    the effort. Learn to do it right, but also learn when good enough is
    good enough.
     
    Joe Kesselman, Apr 14, 2007
    #12
  13. CI

    roy axenov Guest

    I don't know about you, but premature optimization is a sin
    in my book. Go for clarity and neatness first, deal with
    performance issues as they arise.
     
    roy axenov, Apr 14, 2007
    #13
  14. Especially when still teaching basic concepts.

    Steve Boies: "Make it work, make it good, make it great."
    (Chorus response: "In that order!")
     
    Joe Kesselman, Apr 14, 2007
    #14
  15. Yes, and following all these recommendations I'd still prefer the most
    compact and elegant solution (sorry, it happens to be the one I proposed
    ) )

    I didn't spend any time trying to optimize -- just chose the most *natural*
    solution, which for XSLT happens to be overriding the identity rule -- and
    this is so for more than 90% of all cases.


    Cheers,
    Dimitre Novatchev.
     
    Dimitre Novatchev, Apr 14, 2007
    #15
  16. I agree that this is the core starting point for most XSLT design.
     
    Joe Kesselman, Apr 14, 2007
    #16
    1. Advertisements

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 (here). After that, you can post your question and our members will help you out.