scalaplayframeworkplayframework-2.6play-json

Difference between validate and validateOpt in JsValue


JsValue has two methods

def validate[A](implicit rds: Reads[A]): JsResult[A] - Tries to convert the node into a JsResult[T] (Success or Error).

def validateOpt[A](implicit rds: Reads[A]): JsResult[Option[A]] - I suppose it also does the same thing.

In which situation would validateOpt be used? In my opinion, if JsResult fails then I'll get the error in JsError. So what is the point of having an additional layer of Option in JsSuccess as JsSuccess will always contain value after successful conversion of JsValue into the type A?


Solution

  • validateOpt should be used when a null JSON value or missing JSON path is not considered an error. For example, say we have the following model

    case class Person(
      name: String
      employer: Option[String]
    )
    

    where employer field is optional as it is perfectly reasonable for a person not to be employed, whilst they always have a name. Then deserialising the following JSON

    {
      "name": "Picard"
    }
    

    should succeed even though employer path is missing. Thus manually defining Reads[Person] would make use of validateOpt like so

      implicit val personRead: Reads[Person] = Reads { json =>
        for {
          name     <- (json \ "name").validate[String]
          employer <- (json \ "employer").validateOpt[String]
        } yield Person(name, employer)
      }
    

    Also contrast deserialisation of null, for example

      val raw = "null"
      val json = Json.parse(raw)
      println(json.validate[String])
      println(json.validateOpt[String])
    

    should output

    JsError(List((,List(JsonValidationError(List(error.expected.jsstring),WrappedArray())))))
    JsSuccess(None,)
    

    where we see validateOpt resulted in success.