Your controller shouldn't need to worry about validating and mapping specific class fields, that's the model's job. Assuming the Example case class you appear to be using, you can easily create a Reads[Example] using the Json.reads macro provided by Play. The implicit Reads should be placed in the companion object of the corresponding class, so the implicit can be picked up from anywhere. You can also create more custom Reads if you need to by reading through the documentation, but for now we'll stick to the basics.
import play.api.libs.json._
case class Example(id: Int, name: String)
object Example {
implicit val reads: Reads[Example] = Json.reads[Example]
}
Then, in your controller, you can use JsValue#validate[A] to attempt to de-serialize the entire model at once. Doing so returns a JsResult[A], which can either be a JsSuccess[A] that holds the de-serialized model, or a JsError. We can fold the result to handle both cases.
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 {
// ...
}
}
)
}
Now, you can easily change the above controller to handle a array instead of a single model by changing:
request.body.validate[List[Example]]
And the second part of the fold method you will get a List[Example] that you can work with.
Note that in your error cases, instead of using Future { ... } to wrap what are essentially constant values that do not block anything, you can instead wrap them in Future.successful(...) to avoid dispatching trivial work out to the ExecutionContext.
{}, it is. I'm assuming that's what you meant.