Kevin Meredith Kevin Meredith - 1 month ago 19
JSON Question

Json#arr w/ List[Int] Input

Looking at this helpful answer on encoding a

List[A]
in the Play JSON library,

I tried to use Json#arr:

import play.api.libs.json._

scala> Json.arr( 1, 2, 3 )
res21: play.api.libs.json.JsArray = [1,2,3]


All works well so far, but what if I have a
List[Int]
:

scala> Json.arr( List(1,2,3) )
res22: play.api.libs.json.JsArray = [[1,2,3]] // not what I wanted


I attempted to pass the
List[Int]
as var-args (if that's the right term):

scala> Json.arr( List(1,2,3): _* )
<console>:18: error: type mismatch;
found : List[Int]
required: Seq[play.api.libs.json.Json.JsValueWrapper]
Json.arr( List(1,2,3): _* )
^


But that did not work.

Then, I tried to create a
List[JsValueWrapper]
, and then pass that, via var-args, to
Json#arr
:

scala> List(1,2,3).map(Json.toJsFieldJsValueWrapper)
<console>:18: error: No Json serializer found for type T. Try to implement an implicit Writes or Format for this type.
List(1,2,3).map(Json.toJsFieldJsValueWrapper)
^


Although that failed, the following works when applied to a single
Int
:

scala> Json.toJsFieldJsValueWrapper(1)
res27: play.api.libs.json.Json.JsValueWrapper = JsValueWrapperImpl(1)

scala> Json.arr(res27)
res28: play.api.libs.json.JsArray = [1]


How can I pass a
List[Int]
into
Json.arr
to get an output of
[1,2,3]
, i.e. a
JsArray
consisting of three
JsNumber
's?

Answer

You could use Json.toJson, validate as a JsArray, or return an empty one if invalid:

scala> val list = List(1, 2, 3)
list: List[Int] = List(1, 2, 3)

scala> Json.toJson(list).validate[JsArray].getOrElse(JsArray())
res6: play.api.libs.json.JsArray = [1,2,3]

You could also modify your lambda slightly with Json.arr:

scala> Json.arr(list.map(Json.toJsFieldJsValueWrapper(_)): _*)
res10: play.api.libs.json.JsArray = [1,2,3]

It seems as though without the parentheses, the compiler is having trouble inferring what T is. This is because the compiler tries to resolve the implicit before eta-expanding the anonymous function, so it doesn't know what argument will be passed to Json.toJsFieldJsValueWrapper yet. Json.toJsFieldJsValueWrapper(_) is fundamentally different because it expands to a slightly different Function1:

 x => Json.toJsFieldJsValueWrapper(x)

Here the compiler knows that x will be an Int, so the implicit Writes[Int] is resolved.