1

After reading this question

I'm wondering how you can generate the JSON value of a Directorate and its collection of ServicesAreas. If we're using the new inception mecanism provided by Play! 2.2 we are only formatting the case class Directorate.

Is the simple answer is to format the case class and the list individually (2 JSON AST) and combine them using Play JSON API to build a single tree ?

thanks a lot for your lights

update 1 : When I wrote about combine I was thinking about Write Combinators. Write[Directorate] and Write[Seq[ServicesArea]] ? Or perhaps when querying with Slick is it easier to build a new case class with Directorate base attributes + a Seq of ServiceAreas ?

Community
  • 1
  • 1
Archange
  • 397
  • 5
  • 21

1 Answers1

1

I would say you have two good options and neither has got anything to do with that you are using Slick (which is good, how you store stuff in a database should not be tightly coupled with how you turn it into some serialized format, right?).

You either represent the json format you are after with the corresponding class structure, using case classes and the Json.format macro. You can of course create a separate class hierarchy from your "model" class structure, because you may want to structure things differently, hide fields etc. It could also be good to avoid having a model refactoring, say, changing a property name change the json format, especially if you have external users of your json.

The other option is to gather the data in some generic data structures, say a List[(Directorate, Seq[ServicesArea])] or a Map[(Directorate, Seq[ServicesArea])] and create either a Writes[thattype] or maybe even as easy as just using the json api to build the json format you want given that data, something like this:

...
JsArray(entries.map { case (directorate, serviceAreas) =>
  Json.obj(
    "somekey" -> directorate.someProp,
    "areas" -> JsArray(serviceAreas.map { area => 
      Json.obj(
        "areakey" -> area.something
      )}
  )
})
...

Of course using the combinator api to create a writes is about as easy and might be more concise but a bit harder to understand (except maybe if you have used the combinator api before), just another way to implement the same idea.

...
implicit val serviceAreaWrites: Writes[ServiceArea] = (
  (__ \ "areakey").write[String] and 
  (__ \ "someotherkey").write[String]
)(unlift(ServiceArea.unapply)) 
// note that this must return the exact list of fields you have listed

implicit val directorateWrites: Writes[(Directorate, Seq[ServiceArea])] = (
  (__ \ "somekey").write[String] and 
  (__ \ "areas").write[Seq[ServiceArea]]
)(unlift(Tuple2.unapply[Directorate, Seq[ServiceArea]))

Neither of the two is "the right (tm)" way to do it, you will have to think about what pros and cons each has got in your application and choose the best fit.

Good luck!

johanandren
  • 11,249
  • 1
  • 25
  • 30