Caballero Caballero - 4 months ago 79
JSON Question

Scala/Play: parse JSON into Map instead of JsObject

On Play Framework's homepage they claim that "JSON is a first class citizen". I have yet to see the proof of that.

In my project I'm dealing with some pretty complex JSON structures. This is just a very simple example:

{
"key1": {
"subkey1": {
"k1": "value1"
"k2": [
"val1",
"val2"
"val3"
]
}
}
"key2": [
{
"j1": "v1",
"j2": "v2"
},
{
"j1": "x1",
"j2": "x2"
}
]
}


Now I understand that Play is using Jackson for parsing JSON. I use Jackson in my Java projects and I would do something simple like this:

ObjectMapper mapper = new ObjectMapper();
Map<String, Object> obj = mapper.readValue(jsonString, Map.class);


This would nicely parse my JSON into Map object which is what I want - Map of string and object pairs and would allow me easily to cast array to
ArrayList
.

The same example in Scala/Play would look like this:

val obj: JsValue = Json.parse(jsonString)


This instead gives me a proprietary
JsObject
type which is not really what I'm after.

My question is: can I parse JSON string in Scala/Play to
Map
instead of
JsObject
just as easily as I would do it in Java?

Side question: is there a reason why
JsObject
is used instead of
Map
in Scala/Play?

My stack: Play Framework 2.2.1 / Scala 2.10.3 / Java 8 64bit / Ubuntu 13.10 64bit

UPDATE: I can see that Travis' answer is upvoted, so I guess it makes sense to everybody, but I still fail to see how that can be applied to solve my problem. Say we have this example (jsonString):

[
{
"key1": "v1",
"key2": "v2"
},
{
"key1": "x1",
"key2": "x2"
}
]


Well, according to all the directions, I now should put in all that boilerplate that I otherwise don't understand the purpose of:

case class MyJson(key1: String, key2: String)
implicit val MyJsonReads = Json.reads[MyJson]
val result = Json.parse(jsonString).as[List[MyJson]]


Looks good to go, huh? But wait a minute, there comes another element into the array which totally ruins this approach:

[
{
"key1": "v1",
"key2": "v2"
},
{
"key1": "x1",
"key2": "x2"
},
{
"key1": "y1",
"key2": {
"subkey1": "subval1",
"subkey2": "subval2"
}
}
]


The third element no longer matches my defined case class - I'm at square one again. I am able to use such and much more complicated JSON structures in Java everyday, does Scala suggest that I should simplify my JSONs in order to fit it's "type safe" policy? Correct me if I'm wrong, but I though that language should serve the data, not the other way around?

UPDATE2: Solution is to use Jackson module for scala (example in my answer).

Answer

I've chosen to use Jackson module for scala.

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import com.fasterxml.jackson.module.scala.experimental.ScalaObjectMapper

val mapper = new ObjectMapper() with ScalaObjectMapper
mapper.registerModule(DefaultScalaModule)
val obj = mapper.readValue[Map[String, Object]](jsonString)
Comments