es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Validaciones personalizadas basadas en otra validación.

Nos gustaría basar algunas de nuestras validaciones JSON en el resultado de la validación de una validación anterior.

Tenemos la clase de caso anterior y nos gustaría introducir una validación JSON que haga que el campo “name” sea obligatorio si “eType = 1” o que el campo “companyName” sea obligatorio si “eType = 2”, además de algunas validaciones específicas por campo que ya tenemos en su lugar al leer el objeto JSON.

implicit val insideObjectReads: Reads[InsideObject] = (
    (JsPath \ "name").readNullable[String]
        .filter(JsonValidationError("debe tener 10 dígitos"))(name => name.getOrElse("").length == 10) and
    (JsPath \ "companyName").readNullable[String]
        .filter(JsonValidationError("no debe ser opcional"))(companyName => companyName.isDefined) and
    (JsPath \ "eType").read[Int]
        .filter(JsonValidationError("eType solo puede tener los valores 1 o 2"))(eType => eType == 1 || eType == 2)
)(InsideObject.apply _)

Podemos hacer estas validaciones dentro del método “apply”, pero nos gustaría hacerlo mientras se lee el objeto JSON. ¿Alguna sugerencia?

Código de muestra: https://scastie.scala-lang.org/wTuhI1zCSJSWKu9Lltcqbw

Tags:  , ,

Answer

  1. Avatar for davy.ai

    Para incorporar los resultados de validación anteriores en la validación JSON, podemos usar una combinación de las funciones flatMap y orElse proporcionadas por la biblioteca Play JSON.

    Primero, podemos crear dos funciones de validación separadas para name y companyName:

    def validateName(name: Option[String], eType: Int): JsResult[String] = {
      if (eType == 1 && name.isEmpty) {
        JsError("name es requerido para eType = 1")
      } else {
        JsSuccess(name.getOrElse("nombre desconocido"))
      }
    }
    
    def validateCompanyName(companyName: Option[String], eType: Int): JsResult[String] = {
      if (eType == 2 && companyName.isEmpty) {
        JsError("companyName es requerido para eType = 2")
      } else {
        JsSuccess(companyName.getOrElse("compañía desconocida"))
      }
    }
    

    Estas funciones reciben los valores name, companyName y eType y devuelven un JsResult[String], que puede ser tanto un JsSuccess con el valor validado como un JsError con un mensaje de error de validación.

    A continuación, podemos modificar insideObjectReads para incorporar estas funciones de validación utilizando las funciones flatMap y orElse:

    implicit val insideObjectReads: Reads[InsideObject] = (
      (JsPath \ "name").readNullable[String]
        .flatMap(name => validateName(name, (JsPath \ "eType").read[Int].reads(json)))
        .filter(JsonValidationError("debe tener 10 dígitos"))(name => name.length == 10) and
      (JsPath \ "companyName").readNullable[String]
        .flatMap(companyName => validateCompanyName(companyName, (JsPath \ "eType").read[Int].reads(json)))
        .filter(JsonValidationError("no debe ser opcional"))(companyName => companyName.isDefined) and
      (JsPath \ "eType").read[Int]
        .filter(JsonValidationError("eType solo puede tener valores 1 o 2"))(eType => eType == 1 || eType == 2)
    )(InsideObject.apply _)
    

    Aquí, primero usamos flatMap para llamar a validateName y validateCompanyName en name y companyName, respectivamente, pasando el resultado de la validación de eType utilizando la función reads(json) como segundo argumento. Estas funciones devuelven un JsResult[String], que luego se pasa a la función orElse para recurrir al valor original si la validación falla. Finalmente, añadimos las validaciones restantes como antes.

    Con esta modificación, el campo name será requerido solo cuando eType = 1 y de manera similar, companyName será requerido solo cuando eType = 2.

Comments are closed.