In Python I can arbitrarily nest lists and dictionaries and use a mixture of types and then simply call json.dumps(x)
and get the result I need.
I can't find anything like this in Scala. All the libraries I come across seem to insist on static typing and compile-time checks. I would rather give that up for simplicity. It seems that it should be possible to dynamically check the types of the inputs.
As a simple example, I would like to be able to do something like:
toJson(Map("count" -> 1,
"objects" -> Seq(Map("bool_val" -> true,
"string_val" -> "hello"))))
which would output a string containing:
{"count": 1, "objects": [{"bool_val": true, "string_val": "hello"}]}
EDIT: Here is what happens when I try a couple of libraries:
scala> import upickle.default._
import upickle.default._
scala> write(Seq(1, 2))
res0: String = [1,2]
scala> write(Seq(1, "2"))
<console>:15: error: Couldn't derive type Seq[Any]
write(Seq(1, "2"))
^
scala> import spray.json._
import spray.json._
scala> import spray.json.DefaultJsonProtocol._
import spray.json.DefaultJsonProtocol._
scala> Seq(1, 2).toJson
res2: spray.json.JsValue = [1,2]
scala> Seq(1, "2").toJson
<console>:21: error: Cannot find JsonWriter or JsonFormat type class for Seq[Any]
Seq(1, "2").toJson
^
I tried creating my own general protocol for spray but I'm getting weird results:
import spray.json._
object MyJsonProtocol extends DefaultJsonProtocol {
implicit object AnyJsonWriter extends JsonFormat[Any] {
def write(x: Any) = x match {
case n: Int => JsNumber(n)
case n: Long => JsNumber(n)
case d: Double => JsNumber(d)
case s: String => JsString(s)
case b: Boolean if b => JsTrue
case b: Boolean if !b => JsFalse
case m: Map[Any, Any] => m.toJson
case p: Product => p.productIterator.toList.toJson // for tuples
case s: Seq[Any] => s.toJson
case a: Array[Any] => a.toJson
}
override def read(json: JsValue) = ???
}
}
import MyJsonProtocol._
val objects = Seq(Map(
"bool_val" -> true,
"string_val" -> "hello"))
objects.toJson.toString
// [{"bool_val":true,"string_val":"hello"}]
Map(
"count" -> 1,
"objects" -> objects).toJson.toString
// {"count":1,"objects":[{"bool_val":true,"string_val":"hello"},[]]}
// I don't know where this comes from: ^