Iterating over an array in JSON with Play 2.1.1

北城以北 提交于 2019-11-28 11:43:16

Your json value is an object with field data containing an array. You are trying to parse the single object as an array. You would either have to change json to.

val json = Json.parse("[{\"name\": \"Me Lazyan\",\"id\": \"1182\"},{\"name\": \"Chales Dselle\",\"id\": \"10115\"},{\"name\": \"Be My\",\"id\": \"10275\"},{\"name\": \"De Rwani\", \"id\": \"11189\"},{\"name\": \"Phoe Johon\", \"id\": \"11372\"}]")

or change your code to

val people = (json \ "data").validate[List[FBUser]].get

Purpose

Even if the the @martin's answer is giving the straight solution regarding the mismatch between the json message and the expected result, I'd like to give you some advices for a clean way to reach your goal.

That's because, AFAICK, you're redefining way too much things and you could meet cases where errors aren't reported... but just hidden by a different error like NoSuchElementException.

Lastly, I would like to give a solution where your message is kept unchanged (like the second @Martin 's solution was).

Proposition

The idea is to keep things packaged with their responsibilities also the code is split in three different files:

FBUser.scala which simply declares the model structure

package models

case class FBUser(
    name: String,
    id: String
)

formats.scala which is a package that will gather all formats definition, specially the json ones:

package models

import play.api.libs.json._
import play.api.libs.json.Reads._
import play.api.libs.json.util._
import play.api.libs.json.Json._
import play.api.libs.functional.syntax._

package formats {

  object jsons {
    implicit val fbUserFormat:Format[FBUser] = Json.format[FBUser]
  }

}

Note that the format for List[FBUser] is not provided because the json api will resolve to the implicit and generic Reads and OWrites.

And lastly, the Application.scala that will contain code only relevant to some use cases, like the json object containing a data field, the list of FBUser.

package controllers

import play.api._
import play.api.mvc._
import play.api.libs.json._
import play.api.libs.json.Json._

import models._

object Application extends Controller {

  import models.formats.jsons._
  val readUserFromInput = (__ \ 'data).read[List[FBUser]]

  def index = Action {
    val jsonString = "{\"data\": [{\"name\": \"Me Lazyan\",\"id\": \"1182\"},{\"name\": \"Chales Dselle\",\"id\": \"10115\"},{\"name\": \"Be My\",\"id\": \"10275\"},{\"name\": \"De Rwani\", \"id\": \"11189\"},{\"name\": \"Phoe Johon\", \"id\": \"11372\"}]}"

    val json = Json.parse(jsonString)
    val users = json.validate(readUserFromInput)

    users.map(
      list => Ok(Json.toJson(list)) // or Ok(json) to match exactly you example
    ).recoverTotal{
      err => BadRequest(JsError.toFlatJson(err))
    }
  }

}

In this controller, we can see that it defines the specific Reads for the initial case, thus the access to the data field and the reads to FBUser instance are safe thanks to the use of validate, map and recoverTotal.

Some words

A last note about safety, Json.parse is not safe so in order to be even more safe you should consider some options depending on your workflows, some of them are: * using the json body parser that'll let you handle bad json format explicitly * using a dedicated case class defined in the controller for this particular case (and then define its Reads/... using inception like for FBUser)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!