我如何将一组json
个对象解析为scala List
或Array
?
现在我有一个解析单个对象的代码:
{"id":1,"name":"example1"}
这是代码:
def exampleAction = Action.async(parse.json) { implicit request =>
for {
id <- (request.body \ "id").asOpt[Int]
name <- (request.body \ "name").asOpt[String]
} yield {
(exampleService.create(Example(id, name)) map { n => Created("Id of Object Added : " + n) }).recoverWith {
case e => Future {
InternalServerError("There was an error at the server")
}
}
}.getOrElse(Future { BadRequest("Wrong json format") })
}
但是我应该如何更改它以解析像这样的json请求:
{[{"id":1,"name":"example1"},{"id":2,"name":"example2"}]}
我想函数map
应该在某个地方使用。
答案 0 :(得分:6)
您的控制器不应该担心验证和映射特定类字段,这是该模型的工作。假设您似乎正在使用Example
案例类,您可以使用Play提供的Reads[Example]
宏轻松创建Json.reads
。隐式Reads
应该放在相应类的伴随对象中,因此隐式可以从任何地方获取。如果您需要阅读documentation,还可以创建更多自定义Reads
,但目前我们仍然坚持基础知识。
import play.api.libs.json._
case class Example(id: Int, name: String)
object Example {
implicit val reads: Reads[Example] = Json.reads[Example]
}
然后,在您的控制器中,您可以使用JsValue#validate[A]
尝试一次反序列化整个模型。这样做会返回JsResult[A]
,JsSuccess[A]
可以是包含反序列化模型的JsError
,也可以是fold
。我们可以def exampleAction = Action.async(parse.json) { implicit request =>
request.body.validate[Example].fold(
error => Future.successful(InternalServerError("JSON did not validate.")),
example => {
// Do something with the case class
exampleService.create(example).map {
// ...
} recoverWith {
// ...
}
}
)
}
结果来处理这两种情况。
request.body.validate[List[Example]]
现在,您可以通过更改以下内容轻松更改上述控制器以处理数组而不是单个模型:
fold
List[Example]
方法的第二部分,您将获得可以使用的Future { ... }
。
请注意,在您的错误情况下,您可以将Future.successful(...)
包裹起来,而不是使用ExecutionContext
来包装基本上不会阻止任何内容的常量值,而是将它们包装在collections.defaultdict
中以避免将繁琐的工作分配给from collections import defaultdict
items = [
[4, "apples"],
[3, "oranges"],
[4, "bananas"],
[2, "apples"],
[2, "pineapple"],
[3, "apples"]
]
mapping = defaultdict(list)
for number, name in items:
mapping[number].append(name)
。
答案 1 :(得分:2)
借用迈克尔的答案,我们可以使用parse.json
that is parameterized in its type版本进一步简化控制器代码。
假设Reads
存在:
import play.api.libs.json._
case class Example(id: Int, name: String)
object Example {
implicit val reads: Reads[Example] = Json.reads[Example]
}
要处理Example对象的json数组,您只需使用parse.json[List[Example]]
作为身体解析器,然后request.body
将成为List[Example]
:
def exampleAction = Action.async(parse.json[List[Example]]) { implicit request =>
val examples: List[Example] = request.body
// ...
}
如果发布到此端点的正文不是示例对象的有效json数组,则播放将自动返回400 Bad Request
。
答案 2 :(得分:1)
我尝试按照接受的答案和greggz'来写信,但无法让他们编译。
我正在建立一个gregghz答案的答案。所以......你肯定需要像他一样制作案例类和伴侣对象:
import play.api.libs.json._
case class Example(id: Int, name: String)
object Example {
implicit val reads: Reads[Example] = Json.reads[Example]
}
但是你的控制器方法可以更简单,特别是如果你不想乱搞Async。这对我有用,因为我从客户端发送了一个HTML form
,每个input
只发送回这个方法。这种方式可能存在缺点,我很感激在评论中听到它们,但你要做的就是:
def exampleAction = Action {
implicit request: Request[AnyContent] => {
val jsonBody = request.body.asJson
val myContent : List[Example] = jsonBody.get.as[List[Example]]
// ... do rest of work here
Ok("Json parsed")
}
}