João Gonçalves
João Gonçalves

Reputation: 4002

Why can't I serialize Map[String,Any] to JSON?

I can't seem to find a proper way to serialize my Map to a JSON object, when it has different types for the value, i.e.:

var user: Map[String, Any] = Map("name" -> "1", "id" -> 1)
val json: JsValue = Json.obj(
  "user" -> user
)

produces:

type mismatch;
 found   : Map[String,Any]
 required: play.api.libs.json.Json.JsValueWrapper

Or, if I use instead Map[String,AnyVal], it produces:

the result type of an implicit conversion must be more specific than AnyVal

But if I use either Map[String,Number] or Map[String,String] it works.

What am I missing, is there some wrapper I can call to safely use both numbers and integers on my JSON.obj() call?

Upvotes: 3

Views: 4142

Answers (1)

nattyddubbs
nattyddubbs

Reputation: 2095

Given your code snippet you will need to do the following:

var user = Map("name" -> "1", "id" -> 1)
var json: JsValue = Json.obj(
    "user" -> Json.toJson(user)
)

The next problem that will arise is that Play cannot find an implicit writes for type Map[String, Any].

I'd recommend writing your own Writes for the object you're trying to serialize. Maybe something like the below:

case class User(props: Map[String, Any])

implicit val writes: Writes[User] = new Writes[User]{
  override def writes(o: User): JsValue = {
    val propsJs = JsObject(
      o.props.map{kvp =>
        kvp._1 -> (kvp._2 match {
          case x: String => JsString(x)
          case x: Int => JsNumber(x)
          case _ => JsNull // Do whatever you want here.
        })
      }
    )
    JsObject(Map("user" -> propsJs))
  }
}

With a test:

val u1 = User(Map("name" -> "1", "id" -> 1))

val js = Json.toJson(u1)

out: js: play.api.libs.json.JsValue = {"user":{"name":"1","id":1}}

EDIT:

As pointed out in the comment, it may be advantageous (depending on your use case) to do the following:

case class User(name: String, id: Int)

implicit val writes = Json.writes[User]

This is a more readable and better solidifies the definition of a User. It may be possible that you cannot do this however if you can I (and others) would suggest it.

Upvotes: 5

Related Questions