在Python中,我可以任意嵌套列表和字典,并使用各种类型的混合,然后只需调用json.dumps(x)
并获得我需要的结果。
我在Scala中找不到这样的东西。我遇到的所有库似乎都坚持静态类型和编译时检查。我宁愿为了简单而放弃。似乎应该可以动态检查输入的类型。
作为一个简单的例子,我希望能够做到这样的事情:
toJson(Map("count" -> 1,
"objects" -> Seq(Map("bool_val" -> true,
"string_val" -> "hello"))))
将输出包含以下内容的字符串:
{"count": 1, "objects": [{"bool_val": true, "string_val": "hello"}]}
编辑:当我尝试几个库时会发生什么:
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
^
我尝试创建自己的喷雾通用协议但是我得到了奇怪的结果:
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: ^
答案 0 :(得分:0)
如果你只想扔掉类型系统并投射一切:
import math._ // For BigDecimal and the associated implicit conversions
// Dispatch to another overload if possible, or crash and burn with MatchError
def toJson(any: Any): String = any 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 to BD so toJson(Number) doesn't need to be 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 a list of "key": "value" entries
obj.foldRight(Seq.empty[String]) { case ((prop, value), building) =>
s"${toJson(prop)}: ${toJson(value)}" +: building
}.mkString("{ ", ", ", " }")
// Wrap in { ... } and 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 [ ... ] and add ", "s
// Process each character, one by 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"
// All the 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
请注意,这是一个非常非Scala的事情。 Python没有像Scala这样的类型系统,这就是为什么你习惯于运行时检查这样的代码。但Scala不是那样的,你应该与一起工作,而不是反对它。首先,像这样创建包含Any
的地图和seq是一个巨大的红旗。考虑使用case class
es来保存数据。为此,许多Scala的JSON库支持自动派生的JSON编码/解码器,通常使用shapeless.Generic
。
答案 1 :(得分:0)
基于https://coderwall.com/p/o--apg/easy-json-un-marshalling-in-scala-with-jackson:
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)
def toJson(value: Any): String = {
mapper.writeValueAsString(value)
}