Play Framework 2.1 中的 Scala 到 JSON
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14467689/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Scala to JSON in Play Framework 2.1
提问by George Hernando
I'm trying to convert Scala to JSON in the 2.1RC Play Framework.
我正在尝试在 2.1RC Play 框架中将 Scala 转换为 JSON。
I can do the following and get JSON:
我可以执行以下操作并获得 JSON:
import play.api.libs.json._
val a1=Map("val1"->"a", "val2"->"b")
Json.toJSon(a1)
Because a1 is just Map[String,String] that works OK.
因为 a1 只是 Map[String,String] 可以正常工作。
But if I have something more complex like where I have Map[String,Object], that doesn't work:
但是如果我有更复杂的东西,比如我有 Map[String,Object],那就行不通了:
val a = Map("val1" -> "xxx", "val2"-> List("a", "b", "c"))
Json.toJSon(a1)
>>> error: No Json deserializer found for type scala.collection.immutable.Map[String,Object]
I found that I can do something like the following:
我发现我可以执行以下操作:
val a2 = Map("val1" -> Json.toJson("a"), "val2" -> Json.toJson(List("a", "b", "c")))
Json.toJson(a2)
And that works.
这有效。
But how can I do that in a general way? I thought that I could do something like the following:
但是我怎么能以一般的方式做到这一点?我认为我可以执行以下操作:
a.map{ case(k,v)=> (k, Json.toJson(v) )}
>>> error: No Json deserializer found for type Object
But I still get an error that it can't be deserialized
但我仍然收到无法反序列化的错误
Additional Information:
附加信息:
Json.toJson can convert a Map[String, String] to a JsValue:
Json.toJson 可以将 Map[String, String] 转换为 JsValue:
scala> val b = Map( "1" -> "A", "2" -> "B", "3" -> "C", "4" -> "D" )
b: scala.collection.immutable.Map[String,String] = Map(1 -> A, 2 -> B, 3 -> C, 4 -> D)
scala> Json.toJson(b)
res31: play.api.libs.json.JsValue = {"1":"A","2":"B","3":"C","4":"D"}
But, it fails in trying to convert a Map[String, Object]:
但是,它在尝试转换 Map[String, Object] 时失败:
scala> a
res34: scala.collection.immutable.Map[String,Object] = Map(val1 -> xxx, val2 -> List(a, b, c))
scala> Json.toJson(a)
<console>:12: error: No Json deserializer found for type scala.collection.immutable.Map[String,Object]. Try to implement an implicit Writes or Format for this type.
Json.toJson(a)
Using the 'hint' from this Play Framework page on converting Scala to Json, I found the following (http://www.playframework.org/documentation/2.0.1/ScalaJson):
使用此 Play Framework 页面中关于将 Scala 转换为 Json 的“提示”,我发现了以下内容(http://www.playframework.org/documentation/2.0.1/ScalaJson):
If instead of Map[String, Object], there is a Map[String, JsValue], then Json.toJson() will work:
如果不是 Map[String, Object],而是 Map[String, JsValue],那么 Json.toJson() 将起作用:
scala> val c = Map("aa" -> Json.toJson("xxxx"), "bb" -> Json.toJson( List("11", "22", "33") ) )
c: scala.collection.immutable.Map[String,play.api.libs.json.JsValue] = Map(aa -> "xxxx", bb -> ["11","22","33"])
scala> Json.toJson(c)
res36: play.api.libs.json.JsValue = {"aa":"xxxx","bb":["11","22","33"]}
So, what I would like, is that given a Map[String, Object], where I know that the Object values were all originally of type String or List[String], how to apply the function Json.toJson() to the all the values in the map and get a Map[String, JsValue].
所以,我想要的是,给定一个 Map[String, Object],其中我知道 Object 值最初都是 String 或 List[String] 类型,如何将函数 Json.toJson() 应用于所有地图中的值并获得一个 Map[String, JsValue]。
I also found that I can filter out those values that are purely string and those that are (were) of type List[String]:
我还发现我可以过滤掉那些纯粹是字符串的值和那些(曾经是)List[String] 类型的值:
scala> val a1 = a.filter({case(k,v) => v.isInstanceOf[String]})
a1: scala.collection.immutable.Map[String,Object] = Map(val1 -> xxx)
scala> val a2 = a.filter({case(k,v) => v.isInstanceOf[List[String]]})
<console>:11: warning: non-variable type argument String in type List[String] is unchecked since it is eliminated by erasure
val a2 = a.filter({case(k,v) => v.isInstanceOf[List[String]]})
^
a2: scala.collection.immutable.Map[String,Object] = Map(val2 -> List(a, b, c))
The List[String] filtering gives a warning, but seems to give the answer I want. If the two filters could be applied and then Json.toJson() used on the values of the result, and the results combined, maybe that would work?
List[String] 过滤给出了警告,但似乎给出了我想要的答案。如果可以应用这两个过滤器,然后将 Json.toJson() 用于结果的值,并将结果组合起来,也许会起作用?
But the filtered results are still of type Map[String, Object] which causes a problem:
但是过滤后的结果仍然是 Map[String, Object] 类型,这就导致了一个问题:
scala> Json.toJson(a1)
<console>:13: error: No Json deserializer found for type scala.collection.immutable.Map[String,Object]. Try to implement an implicit Writes or Format for this type.
Json.toJson(a1)
回答by sndyuk
Play 2.1 JSON API does not provide a serializer for the Type Map[String, Ojbect].
Play 2.1 JSON API 没有为 Type 提供序列化器Map[String, Ojbect]。
Define case classand Formatfor the specific type instead of Map[String, Object]:
定义case class和Format为特定类型而不是Map[String, Object]:
// { "val1" : "xxx", "val2" : ["a", "b", "c"] }
case class Hoge(val1: String, val2: List[String])
implicit val hogeFormat = Json.format[Hoge]
If you don't want to create case class. The following code provides JSON serializer/deserializer for Map[String, Object]:
如果您不想创建案例类。以下代码为 Map[String, Object] 提供了 JSON 序列化器/反序列化器:
implicit val objectMapFormat = new Format[Map[String, Object]] {
def writes(map: Map[String, Object]): JsValue =
Json.obj(
"val1" -> map("val1").asInstanceOf[String],
"val2" -> map("val2").asInstanceOf[List[String]]
)
def reads(jv: JsValue): JsResult[Map[String, Object]] =
JsSuccess(Map("val1" -> (jv \ "val1").as[String], "val2" -> (jv \ "val2").as[List[String]]))
}
More dynamically
更动态
import play.api.libs.json._
import play.api.libs.json.Reads._
import play.api.libs.json.Json.JsValueWrapper
implicit val objectMapFormat = new Format[Map[String, Object]] {
def writes(map: Map[String, Object]): JsValue =
Json.obj(map.map{case (s, o) =>
val ret:(String, JsValueWrapper) = o match {
case _:String => s -> JsString(o.asInstanceOf[String])
case _ => s -> JsArray(o.asInstanceOf[List[String]].map(JsString(_)))
}
ret
}.toSeq:_*)
def reads(jv: JsValue): JsResult[Map[String, Object]] =
JsSuccess(jv.as[Map[String, JsValue]].map{case (k, v) =>
k -> (v match {
case s:JsString => s.as[String]
case l => l.as[List[String]]
})
})
}
Sample code:
示例代码:
val jv = Json.toJson(Map("val1" -> "xxx", "val2" -> List("a", "b", "c"), "val3" -> "sss", "val4" -> List("d", "e", "f")))
println(jv)
val jr = Json.fromJson[Map[String, Object]](jv)
println(jr.get)
The output:
输出:
> {"val1":"xxx","val2":["a","b","c"],"val3":"sss","val4":["d","e","f"]}
> Map(val1 -> xxx, val2 -> List(a, b, c), val3 -> sss, val4 -> List(d, e, f))

