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 class
es hold data. purpose, lot of json libraries scala support auto-deriving json en-/de-coders, shapeless.generic
.
Comments
Post a Comment