Json format for tuples in play json
I was in a situation that I needed a list of three (maybe less) related values (the three measures of a physical object).
I considered using a List to hold them but I did not like this solution as a List has an arbitrary length and, in my case, I don't want to have more than three values.
Another solution I considere was a case class Dimensions(1:Option[Measure], 2:Option[Measure], 3: Option[Measure])
but I thought that it would be overkill and, besides, I don't know in advance which is the height, which the width, etc. so I had to use these awful field names (1,2 and 3).
Finally, I decided to use a tuple (Option[Measure], Option[Measure], Option[Measure]) as a lightweight alternative to the case class.
When it came to serializing the value, play-json macros complained that I did not have a formatter for (Option[Measure], Option[Measure], Option[Measure]). After some googling I found this thread https://groups.google.com/forum/#!topic/play-framework/kn-gRW8KSwQ where it is explained why this is not provided by default (spoiler alert: JSON's lack of support for tuples has something to do with it :) ) and, fortunately for me, a code example of how to solve the problem :)
implicit def tuple3Reads[A, B, C](implicit aReads: Reads[A], bReads: Reads[B], cReads: Reads[C]): Reads[Tuple3[A, B, C]] = Reads[Tuple3[A, B, C]] {
case JsArray(arr) if arr.size == 3 => for {
a <- aReads.reads(arr(0))
b <- bReads.reads(arr(1))
c <- cReads.reads(arr(2))
} yield (a, b, c)
case _ => JsError(Seq(JsPath() -> Seq(ValidationError("Expected array of three elements"))))
}
implicit def tuple2Writes[A, B, C](implicit aWrites: Writes[A], bWrites: Writes[B], cWrites: Writes[C]): Writes[Tuple3[A, B, C]] = new Writes[Tuple3[A, B, C]] {
def writes(tuple: Tuple3[A, B, C]) = JsArray(Seq(aWrites.writes(tuple._1), bWrites.writes(tuple._2), cWrites.writes(tuple._3)))
}
If you don't like the convention (map a tuple to a JSON array) you can always use JSON combinators as described here: http://www.playframework.com/documentation/2.1-RC2/ScalaJsonCombinators
Written by Jose Raya
Related protips
1 Response
I used case JsArray(List(a, b, c))
instead of if arr.size == 3
.