groovyxml-parsingxmlslurpergpath

Groovy find nodes using gpath with certian child count and expression


Suppose I have an XML:

<?xml version="1.0" encoding="UTF-8"?>
<data>
    <level0 id="1" t="0">
      <level1 id="lev1id01" att1="2015-05-12" val="12" status="0"/>
      <level1 id="lev1id02" att1="2015-06-13" val="13" status="0"/>
      <level1 id="lev1id03" att1="2015-07-10" val="13" status="0"/>
    </level0>

    <level0 id="2" t="0">
        <level1 id="lev1id11" att1="2015-05-12" val="121" status="0"/>
        <level1 id="lev1id12" att1="2015-06-13" val="132" status="0"/>
        <level1 id="lev1id13" att1="2015-07-11" val="113" status="0"/>
    </level0>

    <level0 id="2" t="1">
        <level1 id="lev1id21" att1="2015-05-12" val="121" status="0"/>
        <level1 id="lev1id22" att1="2015-06-13" val="132" status="0"/>
        <level1 id="lev1id23" att1="2015-07-11" val="113" status="0"/>
        <level1 id="lev1id23" att1="2015-07-11" val="113" status="0"/>
    </level0>
</data>

I want to get all level0 nodes (using GPath) which are:

  1. If level0/@t="0" then select this node (level0) only if all its level1 children has @status="0"
  2. If level0/@t!="0" then select this node (level0) only if the last level1 child has @status="0". When I say last I mean the level1 node with maximum value in @att1 (assuming @att1 contains date in yyyy-mm-dd format).

With XPath I would use functions like max() and count(), but I can't get how it could be done using GPath.

Thanks


Solution

  • The max() and count() functions defined by Groovy on Iterable can be used within GPath expressions in place of their XPath equivalents.

    // This closure is for level0[t=0] elements.
    // It selects the level0 if the count of its level1[status=0] children is 0.
    def t0Select = { level0 -> 
        level0.level1.count { level1 -> level1.@status != '0' } == 0 
    }
    
    // This closure is for level1[t=1] elements.
    // It selects the level0 if its level1 element with the maximum date has a status of "0" 
    def t1Select = { level0 -> 
        level0.level1.max { level1 -> Date.parse('yyyy-MM-dd', level1.@att1.toString()) }?.@status == '0' 
    }
    
    // Parse the XML and delegate to the appropriate closure above as per the t attribute
    def selected = new XmlSlurper().parseText(xml).level0.findAll { level0 -> 
        level0.@t == '0' ? t0Select(level0) : t1Select(level0) 
    }