Search code examples
scalaplayframework-2.0jerkson

Jerkson. Serializing map to JsValue


I am using built-in jerson with playframework 2, and all I want is to serialize map, containing values of different type:

object AppWriter extends Writes[Application] {
    def writes(app: Application): JsValue = {
        Json.toJson(Map(
            "id" -> app.getId.toString,
            "name" -> app.getName,
            "users" -> Seq(1, 2, 3)
        ))
    }
}

In this case I have:

No Json deserializer found for type scala.collection.immutable.Map[java.lang.String,java.lang.Object]. 
Try to implement an implicit Writes or Format for this type

Navigatin through framework code shows that there is serializer for Map[String,V] types implicit def mapWrites[V] .. , but I cannot understand why it doesn't applied.

Can anybody help me?

UPD: I found simple workaround:

object AppWriter extends Writes[Application] {
def writes(app: Application): JsValue = {
    Json.toJson(Map[String, JsValue](
        "id" -> JsNumber(BigDecimal(app.getId)),
        "name" -> JsString(app.getName),
        "users" -> JsArray(Seq(1, 2, 3).map(x => JsNumber(x)))
    ))
}

}

but this is not so elegant...


Solution

  • The standard way to do this is by creating a JsObject from the individual key-value pairs for the fields—not by putting the pairs into a map. For example, assuming your Application looks like this:

    case class Application(getId: Int, getName: String)
    

    You could write:

    import play.api.libs.json._, Json.toJson
    
    implicit object AppWriter extends Writes[Application] {
      def writes(app: Application): JsValue = JsObject(
        Seq(
          "id"    -> JsNumber(app.getId),
          "name"  -> JsString(app.getName),
          "users" -> toJson(Seq(1, 2, 3))
        )
      )
    }
    

    And then:

    scala> toJson(Application(1, "test"))
    res1: play.api.libs.json.JsValue = {"id":1,"name":"test","users":[1,2,3]}
    

    Note that you don't need to spell out how to serialize the Seq[Int]—the default Format instances will do the work for you.