I am using play 2.1.1 and I am having issues iterating through an array. I had read somewhere that you can create a reads for a List[Object] but everytime I try to do this I get an error "No unapply function found"
for the line that says
implicit val userListReads: Reads[List[FBUser]] = Json.reads[List[FBUser]]". The issue is " = Json.reads[List[FBUser]]
I am at a loss of what else to try. Any assistance would be greatly appreciated.
def linkUsers() = Action { implicit request =>
val json = Json.parse("{\"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 peoples = json.validate[List[FBUser]].get
peoples.foreach(println)
Ok(json).withHeaders(CONTENT_TYPE -> "text/json")
}
case class FBUser(
name: String,
id: String
)
object FBUser {
/** Uses a Scala Macro to define the Reads function */
implicit val userReads: Reads[FBUser] = Json.reads[FBUser]
implicit val userListReads: Reads[List[FBUser]] = Json.reads[List[FBUser]]
}
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
)
来源:https://stackoverflow.com/questions/16239403/iterating-over-an-array-in-json-with-play-2-1-1