3

如果可以进行投射,Reads 似乎会为我自动投射。例如,浮点数 -> 整数。例如,如果代码得到如下的 json,

{
"name": "Jack",
"age": 22.4,
"role": "Coder"
}

类 Person 的实例将具有 22 的字段年龄,而不是获得无效的参数异常。如果在这种情况下我确实想要一个例外,那么最好的解决方案是什么?多谢。

case class Person(val name: String, val age: Int, val role: String)

object Person {
implicit val residentReads: Reads[Resident] = (
  (JsPath \ "name").read[String](minLength[String](3)) and
  (JsPath \ "age").read[Int](min(0)) and
  (JsPath \ "role").readNullable[String]
)(Resident.apply _)

...
}
4

1 回答 1

4

查看源代码,我们可以看到原因。任何匹配 a 的值JsNumbertoInt调用了它:

implicit object IntReads extends Reads[Int] {
    def reads(json: JsValue) = json match {
        case JsNumber(n) => JsSuccess(n.toInt)
        case _ => JsError(Seq(JsPath() -> Seq(ValidationError("error.expected.jsnumber"))))
    }
}

为了避免这种情况,我们可以实现一个Reads[Int]不会验证非整数的新方法:

import play.api.libs.json._
import play.api.libs.json.Reads._
import play.api.libs.functional.syntax._
import play.api.data.validation._

implicit object WholeIntReads extends Reads[Int] {
    def reads(json: JsValue) = json match {
        case JsNumber(n) if(n.isValidInt) => JsSuccess(n.toInt)
        case _ => JsError(Seq(JsPath() -> Seq(ValidationError("error.expected.jsnumber"))))
    }
}

并像这样使用它:

case class Person(val name: String, val age: Int, val role: String)

object Person {
    implicit val residentReads: Reads[Person] = (
        (JsPath \ "name").read[String](minLength[String](3)) and
        (JsPath \ "age").read[Int](WholeIntReads keepAnd min(0)) and
        (JsPath \ "role").read[String]
    )(Person.apply _)
}

(您的示例代码中有一些不一致之处,所以我修复了它们以进行编译。唯一相关的行是age。)

结果:

scala> Json.parse("""{"name": "Jack","age": 22.4,"role": "Coder"}""").validate[Person]
res13: play.api.libs.json.JsResult[Person] = JsError(List((/age,List(ValidationError(error.expected.jsnumber,WrappedArray())))))

scala> Json.parse("""{"name": "Jack","age": 22,"role": "Coder"}""").validate[Person]
res14: play.api.libs.json.JsResult[Person] = JsSuccess(Person(Jack,22,Coder),)

scala> Json.parse("""{"name": "Jack","age": -22,"role": "Coder"}""").validate[Person]
res15: play.api.libs.json.JsResult[Person] = JsError(List((/age,List(ValidationError(error.min,WrappedArray(0))))))

请注意,我正在使用validate[T]而不是抛出异常,因为这是最佳实践。这将允许ValidationErrors 在不需要任何异常的情况下累积和处理。

于 2014-11-11T03:01:34.877 回答