I managed to implement form validation with custom constraints, but now I want to do the same thing with JSON data.
How can I apply custom validation rules to a JSON par
The simplest way I can think of would use the filter method from Reads.
Let's say we have some User object that will determine if the user name exists:
object User {
def findByName(name: String): Option[User] = ...
}
You could then construct your Reads like this:
import play.api.libs.json._
import play.api.libs.functional.syntax._
import play.api.data.validation._
case class ExampleCaseClass(username: String, somethingElse: String)
object ExampleCaseClass {
implicit val exampleReads: Reads[ExampleCaseClass] = (
(JsPath \ "username").read[String].filter(ValidationError("User does not exist."))(findByName(_).isDefined) and
(JsPath \ "somethingElse").read[String]
)(ExampleCaseClass.apply _)
}
Your controller function can be simplified using a json BodyParser and fold:
def postNew = Action(parse.json) { implicit request =>
request.body.validate[ExampleCaseClass].fold(
error => BadRequest(JsError.toFlatJson(error)),
obj => {
// Do something with the validated object..
}
)
}
You could also create a separate Reads[String] that will check if the user exists, and explicitly use that Reads[String] within your Reads[ExampleCaseClass]:
val userValidate = Reads.StringReads.filter(ValidationError("User does not exist."))(findByName(_).isDefined)
implicit val exampleReads: Reads[ExampleCaseClass] = (
(JsPath \ "username").read[String](userValidate) and
(JsPath \ "somethingElse").read[String]
)(ExampleCaseClass.apply _)