How can I convert any nested combination of maps, seqs, and basic types to JSON in Scala? -


in python can arbitrarily nest lists , dictionaries , use mixture of types , call json.dumps(x) , result need.

i can't find in scala. libraries come across seem insist on static typing , compile-time checks. rather give simplicity. seems should possible dynamically check types of inputs.

as simple example, able like:

tojson(map("count" -> 1,            "objects" -> seq(map("bool_val" -> true,                                 "string_val" -> "hello")))) 

which output string containing:

{"count": 1, "objects": [{"bool_val": true, "string_val": "hello"}]} 

edit: here happens when try 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 seq[any]        seq(1, "2").tojson                    ^ 

i tried creating own general protocol spray 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  // 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"},[]]} //                          don't know comes from: ^ 

if want throw away type system , cast everything:

import math._ // bigdecimal , associated implicit conversions  // dispatch overload if possible, or crash , burn matcherror def tojson(any: any): string = match {   case null            => "null"   case obj: map[_, _]  => tojson(obj.asinstanceof[map[string, any]])   case arr: seq[_]     => tojson(arr)   case str: string     => tojson(str)   case  bl: boolean    => tojson(bl)   case  bd: bigdecimal => tojson(bd)   // v convert bd tojson(number) doesn't need duplicated   case   l: long       => tojson(l: bigdecimal)   case   i: int        => tojson(i: bigdecimal)   case   s: short      => tojson(s: bigdecimal)   case   c: char       => tojson(c: bigdecimal)   case   b: byte       => tojson(b: bigdecimal)   case   f: float      => tojson(f: bigdecimal)   case   d: double     => tojson(d: bigdecimal) }  def tojson(obj: map[string, any]): string =   // build list of "key": "value" entries   obj.foldright(seq.empty[string]) { case ((prop, value), building) =>     s"${tojson(prop)}: ${tojson(value)}" +: building   }.mkstring("{ ", ", ", " }")   // wrap in { ... } , add ", "s  def tojson(arr: seq[any]): string =   // build list of json strings   arr.foldright(seq.empty[string]) { (value, building) =>     tojson(value) +: building   }.mkstring("[ ", ", ", " ]")   // wrap in [ ... ] , add ", "s  // process each character, 1 one def tojson(str: string): string = str.flatmap {   // common escapes   case '\\' => "\\\\"   case '"'  => "\\\""   case '\b' => "\\b"   case '\f' => "\\f"   case '\n' => "\\n"   case '\r' => "\\r"   case '\t' => "\\t"   // other control characters   case ctrl if ctrl < ' ' => f"\\u${ctrl}%04x"   // nothing special: leave unchanged   case c  => c.tostring }.mkstring("\"", "", "\"") // wrap in "..."  def tojson(bl: boolean): string = bl.tostring  def tojson(bd: bigdecimal): string = bd.tostring 

please note un-scala thing do. python doesn't have type system scala, , that's why used runtime-checked code this. scala isn't that, , should working with typer, not against it. one, creating maps , seqs containing any massive red flag. consider using case classes hold data. purpose, lot of json libraries scala support auto-deriving json en-/de-coders, shapeless.generic.


Comments

Popular posts from this blog

android - InAppBilling registering BroadcastReceiver in AndroidManifest -

python Tkinter Capturing keyboard events save as one single string -

sql server - Why does Linq-to-SQL add unnecessary COUNT()? -