playframework - Play JSON Reads[T]: split a JsArray into multiple subsets -


i have json structure contains array of events. array "polymorphic" in sense there 3 possible event types a, b , c:

{  ...  "events": [    { "eventtype": "a", ...},    { "eventtype": "b", ...},    { "eventtype": "c", ...},    ...  ] } 

the 3 event types don't have same object structure, need different reads them. , apart that, target case class of whole json document distinguishes between events:

case class doc(    ...,    aevents: seq[eventa],    bevents: seq[eventb],    cevents: seq[eventc],    ... ) 

how can define internals of reads[doc] json array events split 3 subsets mapped aevents, bevents , cevents?


what tried far (without being succesful):

first, defined reads[jsarray] transform original jsarray jsarray contains events of particular type:

 def eventreads(eventtypename: string) = new reads[jsarray] {     override def reads(json: jsvalue): jsresult[jsarray] = json match {       case jsarray(seq) =>         val filtered = seq.filter { jsval =>           (jsval \ "eventtype").asopt[string].contains(eventtypename)         }         jssuccess(jsarray(filtered))       case _ => jserror("must array")     }   } 

then idea use within reads[doc]:

implicit val docreads: reads[doc] = (     ...     (__ \ "events").read[jsarray](eventreads("a")).andthen... ,     (__ \ "events").read[jsarray](eventreads("b")).andthen... ,     (__ \ "events").read[jsarray](eventreads("c")).andthen... ,     ... )(doc.apply _) 

however, don't know how go on here. assume andthen part should (in case of event a):

.andthen[seq[eventa]](eventa.reads) 

but doesn't work since expect api create seq[eventa] explicitly passing reads[eventa] instead of reads[seq[eventa]]. , apart that, since i've never got running, i'm not sure if whole approach reasonable in first place.

edit: in case original jsarray contains unknown event types (e.g. d , e), these types should ignored , left out final result (instead of making whole reads fail).

put implicit read every event type like

def eventread[a](et: string, er: reads[a]) = (__ \ "eventtype").read[string].filter(_ == et).andkeep(er)  implicit val eventaread = eventread("a", json.reads[eventa]) implicit val eventbread = eventread("b", json.reads[eventb]) implicit val eventcread = eventread("c", json.reads[eventc]) 

and use reads[doc] (folding event list separate sequences types , apply result doc):

reads[doc] = (__ \ "events").read[list[jsvalue]].map(     _.foldleft[jsresult[ (seq[eventa], seq[eventb], seq[eventc]) ]]( jssuccess( (seq.empty[eventa], seq.empty[eventb], seq.empty[eventc]) ) ){       case (jssuccess(a, _), v) =>          (v.validate[eventa].map(e => a.copy(_1 = e +: a._1)) or v.validate[eventb].map(e => a.copy(_2 = e +: a._2)) or v.validate[eventc].map(e => a.copy(_3 = e +: a._3)))             case (e, _) => e     }     ).flatmap(p => reads[doc]{js => p.map(doc.tupled)}) 

it create doc in 1 pass through events list

jssuccess(doc(list(eventa(a)),list(eventb(b2), eventb(b1)),list(eventc(c))),) 

the source data

val json = json.parse("""{"events": [                         |   { "eventtype": "a", "e": "a"},                         |   { "eventtype": "b", "ev": "b1"},                         |   { "eventtype": "c", "event": "c"},                         |   { "eventtype": "b", "ev": "b2"}                         | ]                         |}                         |""") case class eventa(e: string) case class eventb(ev: string) case class eventc(event: string) 

Comments

Popular posts from this blog

python - How to insert QWidgets in the middle of a Layout? -

python - serve multiple gunicorn django instances under nginx ubuntu -

module - Prestashop displayPaymentReturn hook url -