In my day job, we have to do a lot of system integration, a lot of it consists consuming external feeds , process it and feed it to some other system. We use Mule a lot for this. I was writing a groovy script in mule to parse a xml feed and extract some data, here is how my functional programming investment helped me do it in an elegant way. The XML to parse
<sport reportName="aflRoundMatches" jobId="72451003"> <aflRoundMatches roundNumber="3"> <match matchId="10370301" matchStatus="complete" matchName="Dockers vs Bombers" matchNumber="1" isPreseason="false" isFinal="false" homeSquadId="60" homeSquadName="Fremantle" awaySquadId="50" awaySquadName="Essendon" venueId="70" venueName="Patersons Stadium" venueTimezone="Australia/Perth" utcStartTime="2013-04-12T10:40:00+00:00" localStartTime="2013-04-12T18:40:00+08:00" period="4" periodCompleted="4" periodSeconds="1877" homeSquadGoals="9" homeSquadBehinds="14" homeSquadScore="68" awaySquadGoals="10" awaySquadBehinds="12" awaySquadScore="72"/> <match matchId="10370302" matchStatus="complete" matchName="Kangaroos vs Swans" matchNumber="2" isPreseason="false" isFinal="false" homeSquadId="100" homeSquadName="North Melbourne" awaySquadId="160" awaySquadName="Sydney Swans" venueId="2" venueName="Blundstone Arena" venueTimezone="Australia/Hobart" utcStartTime="2013-04-13T03:45:00+00:00" localStartTime="2013-04-13T13:45:00+10:00" period="4" periodCompleted="4" periodSeconds="1873" homeSquadGoals="13" homeSquadBehinds="14" homeSquadScore="92" awaySquadGoals="20" awaySquadBehinds="11" awaySquadScore="131"/> <match matchId="10370303" matchStatus="complete" matchName="Demons vs Eagles" matchNumber="3" isPreseason="false" isFinal="false" homeSquadId="90" homeSquadName="Melbourne" awaySquadId="150" awaySquadName="West Coast Eagles" venueId="40" venueName="MCG" venueTimezone="Australia/Melbourne" utcStartTime="2013-04-13T04:10:00+00:00" localStartTime="2013-04-13T14:10:00+10:00" period="4" periodCompleted="4" periodSeconds="1878" homeSquadGoals="13" homeSquadBehinds="5" homeSquadScore="83" awaySquadGoals="27" awaySquadBehinds="15" awaySquadScore="177"/> <match matchId="10370304" matchStatus="complete" matchName="Giants vs Saints" matchNumber="4" isPreseason="false" isFinal="false" homeSquadId="1010" homeSquadName="GWS Giants" awaySquadId="130" awaySquadName="St Kilda" venueId="150" venueName="Manuka Oval" venueTimezone="Australia/Canberra" utcStartTime="2013-04-13T06:40:00+00:00" localStartTime="2013-04-13T16:40:00+10:00" period="4" periodCompleted="4" periodSeconds="1949" homeSquadGoals="10" homeSquadBehinds="8" homeSquadScore="68" awaySquadGoals="21" awaySquadBehinds="14" awaySquadScore="140"/> <match matchId="10370305" matchStatus="playing" matchName="Cats vs Blues" matchNumber="5" isPreseason="false" isFinal="false" homeSquadId="70" homeSquadName="Geelong Cats" awaySquadId="30" awaySquadName="Carlton" venueId="190" venueName="Etihad Stadium" venueTimezone="Australia/Melbourne" utcStartTime="2013-04-13T09:40:00+00:00" localStartTime="2013-04-13T19:40:00+10:00" period="3" periodCompleted="2" periodSeconds="421" homeSquadGoals="13" homeSquadBehinds="6" homeSquadScore="84" awaySquadGoals="10" awaySquadBehinds="8" awaySquadScore="68"/> <match matchId="10370306" matchStatus="playing" matchName="Suns vs Lions" matchNumber="6" isPreseason="false" isFinal="false" homeSquadId="1000" homeSquadName="Gold Coast Suns" awaySquadId="20" awaySquadName="Brisbane Lions" venueId="81" venueName="Metricon Stadium" venueTimezone="Australia/Brisbane" utcStartTime="2013-04-13T09:40:00+00:00" localStartTime="2013-04-13T19:40:00+10:00" period="3" periodCompleted="2" periodSeconds="773" homeSquadGoals="7" homeSquadBehinds="8" homeSquadScore="50" awaySquadGoals="7" awaySquadBehinds="14" awaySquadScore="56"/> <match matchId="10370307" matchStatus="scheduled" matchName="Tigers vs Bulldogs" matchNumber="7" isPreseason="false" isFinal="false" homeSquadId="120" homeSquadName="Richmond" awaySquadId="140" awaySquadName="Western Bulldogs" venueId="190" venueName="Etihad Stadium" venueTimezone="Australia/Melbourne" utcStartTime="2013-04-14T03:10:00+00:00" localStartTime="2013-04-14T13:10:00+10:00" period="0" periodCompleted="0" periodSeconds="0" homeSquadGoals="0" homeSquadBehinds="0" homeSquadScore="0" awaySquadGoals="0" awaySquadBehinds="0" awaySquadScore="0"/> <match matchId="10370308" matchStatus="scheduled" matchName="Magpies vs Hawks" matchNumber="8" isPreseason="false" isFinal="false" homeSquadId="40" homeSquadName="Collingwood" awaySquadId="80" awaySquadName="Hawthorn" venueId="40" venueName="MCG" venueTimezone="Australia/Melbourne" utcStartTime="2013-04-14T05:20:00+00:00" localStartTime="2013-04-14T15:20:00+10:00" period="0" periodCompleted="0" periodSeconds="0" homeSquadGoals="0" homeSquadBehinds="0" homeSquadScore="0" awaySquadGoals="0" awaySquadBehinds="0" awaySquadScore="0"/> <match matchId="10370309" matchStatus="scheduled" matchName="Power vs Crows" matchNumber="9" isPreseason="false" isFinal="false" homeSquadId="110" homeSquadName="Port Adelaide" awaySquadId="10" awaySquadName="Adelaide Crows" venueId="10" venueName="AAMI Stadium" venueTimezone="Australia/Adelaide" utcStartTime="2013-04-14T06:40:00+00:00" localStartTime="2013-04-14T16:10:00+09:30" period="0" periodCompleted="0" periodSeconds="0" homeSquadGoals="0" homeSquadBehinds="0" homeSquadScore="0" awaySquadGoals="0" awaySquadBehinds="0" awaySquadScore="0"/> </aflRoundMatches> </sport>
The Groovy script:
new XmlSlurper().parseText(payload).aflRoundMatches.match .findAll { ['playing','postmatch'].contains(it.@matchStatus.text()) } .collect { match -> [ 'matchId': match.@matchId.text() , 'homeSquadId' : match.@homeSquadId.text() , 'matchStatus' : match.@matchStatus.text(), 'awaySquadId' : match.@awaySquadId.text(), 'HomeTeam/ScoreTotal_C' : match.@homeSquadScore.text(), 'AwayTeam/ScoreTotal_C' : match.@awaySquadScore.text() , 'Period_Active' : match.@period.text().toInteger() >= match.@periodCompleted.text().toInteger() ? 0 : 1 ] }
Here, new XmlSlurper().parseText(payload).aflRoundMatches.match gives me all the <match> elements of the xml, on that I run a filter for only the ‘playing’ & ‘postmatch’ games (incase you are wondering, its for pushing match scores on Live Television for the playing/postmatch games). After that I run a ‘collect’ method to convert the <match> elements into a Map<attribute, value> for that match. At the end of process I get a List<Map<attribute, Value>> Imagine doing this in Java! This is pure transition of data from one state to another through functions. Coming from years of Object Oriented background, I had to really struggle with this concept as we are so used to think in terms of Nouns aka Classes. Here is a really interesting read about it: http://steve-yegge.blogspot.com.au/2006/03/execution-in-kingdom-of-nouns.html I have some more advanced example which I will save for the next blog post.