播放框架,从请求解析json数组

时间:2017-03-02 20:09:18

标签: arrays json scala playframework

我如何将一组json个对象解析为scala ListArray

现在我有一个解析单个对象的代码:

{"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应该在某个地方使用。

3 个答案:

答案 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")
    }
  }