Reputation: 9734
Given the following JSON...
{
"nickname": "mj",
"firstname": "Mike",
"lastName": "Jordan",
"trash": "ignore"
}
... I need to parse and validate it so that
nickname
, firstName
, and lastName
are filtered out – in my example above trash
has to be removednickname
(which is optional) must be at least 3 characters longHere below is my code:
import play.api.libs.json._
import play.api.libs.functional.syntax._
def orEmpty = reads | __.json.put(Json.obj())
val js = Json.parse("""{ "nickname": "mj", "firstname": "Mike", "lastName": "Jordan" }""")
val validateUser = (
((__ \ 'nickname).json.pickBranch(Reads.of[JsString] <~ Reads.minLength[String](3)) orEmpty) ~
((__ \ 'firstName).json.pickBranch) ~
((__ \ 'lastName).json.pickBranch)
)
validateUser.reads(js).fold(
valid = { validated => JsSuccess(js) },
invalid => { errors => JsError(errors) }
)
The problem is that if nickname
is invalid because shorter than 3 characters, orEmpty
applies and no error is reported. What I need is to keep nickname
optional (that's why I defined orEmpty
), but when defined the validation should succeed if and only if nickanme
passes the Reads.minLength
check.
Upvotes: 1
Views: 1635
Reputation: 7174
Assuming Play Framework 2.4.x. I believe reading nullable fields is a little different in 2.3.x but it's the same idea.
It would be easier to reason about this if you assume that JSON validators and transformers are really just Reads[T <: JsValue]
.
Overall, what you need to consider is that the nickname
field is actually optional and you need to represent it as such. Basically, what you're looking for is to compose the nullableReads
with Reads.minLength
to read nickname as Option[String]
.
So, you can represent the structure you want to validate as:
case class ToValidate(nickname: Option[String], firstname: String, lastname: String)
And define you're Reader:
import play.api.libs.json._
import play.api.libs.json.Reads._
import play.api.libs.functional.syntax._
implicit val toValidateReads: Reads[ToValidate] = (
(__ \ "nickname").readNullable[String](minLength(3)) ~
(__ \ "firstname").read[String] ~
(__ \ "lastname").read[String]
)(ToValidate.apply _)
Then you can vaildate your input:
val js = Json.parse("""{ "nickname": "mj", "firstname": "Mike", "lastname": "Jordan" }""")
val v = js.validate[ToValidate]
println(v) // JsError(List((/nickname,List(ValidationError(List(error.minLength),WrappedArray(3))))))
Upvotes: 2