Last Updated: December 26, 2018
·
10.73K
· joseraya

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

1 Response
Add your response

I used case JsArray(List(a, b, c)) instead of if arr.size == 3.

over 1 year ago ·