lospejos lospejos - 3 months ago 21
Groovy Question

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

Answer

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) 
}
Comments