我正在尝试使用播放框架2.2.2检查Actor中的JsValue对象。当我尝试使用validate方法时,我收到异常而不是结果对象:

try {
      val result = data.validate[EventConfig]
      Logger.debug("Result: "+result")
    } catch {
        case e =>
           Logger.error("Exception: "+e)
    }

这是例外情况:
Exception: play.api.libs.json.JsResultException: JsResultException(errors:List((,List(ValidationError(error.expected.jsnumber,WrappedArray())))))

为什么会发生这种情况,我应如何使用验证方法?

======更新

我正在使用这样的Reads实现:
implicit val EventConfig_reads = new Reads[EventConfig] {
    def reads(json: JsValue): JsResult[EventConfig] = {
        JsSuccess(new
            EventConfig((json \ ConfigEventAttrs.PARAM).as[Int],
              (json \ ConfigEventAttrs.PERIOD).as[Int],
              (json \ ConfigEventAttrs.THRESHOLD).as[Int],
              (json \ ConfigEventAttrs.TOGGLE).as[Boolean]))
    }
  }

解决方案是添加catch子句:
implicit val EventConfig_reads = new Reads[EventConfig] {
    def reads(json: JsValue): JsResult[EventConfig] = {
      try {
        JsSuccess(new
            EventConfig((json \ ConfigEventAttrs.PARAM).as[Int],
              (json \ ConfigEventAttrs.PERIOD).as[Int],
              (json \ ConfigEventAttrs.THRESHOLD).as[Int],
              (json \ ConfigEventAttrs.TOGGLE).as[Boolean]))
      } catch {
        case e: JsResultException =>
          JsError(e.errors)
      }
    }
  }

最佳答案

那不是使用validate的正确方法。我认为文档并没有充分强调它的重要性,但是在Using Validation一节中解释了here
data.validate[EventConfig]返回JsResult而不是 EventConfig。处理错误的首选方法是对结果进行fold:

data.validate[EventConfig].fold(
   error => {
       // There were validation errors, handle them here.
   },
   config => {
       // `EventConfig` has validated, and is now in the scope as `config`, proceed as usual.
   }
)

让我们研究一下。 fold上的JsResult签名如下:
fold[X](invalid: (Seq[(JsPath, Seq[ValidationError])]) ⇒ X, valid: (A) ⇒ X): X

它接受两个函数作为参数,都返回相同类型的结果。第一个函数是Seq[(JsPath, Seq[ValidationError])]) => X。在我上面的代码中,error的类型为Seq[(JsPath, Seq[ValidationError])]),本质上只是一个JSON序列序列,其中包含验证错误。在这里,您可以剖析这些错误,并相应地返回相应的错误消息,或者在失败时执行其他可能需要执行的操作。

第二个函数映射A => X,其中AJsResult类型,在您的情况下为EventConfig。在这里,您将可以直接处理EventConfig类型。

导致异常和捕获异常不是解决此问题的方法(很少这样做),因为您将丢失所有累积的验证错误。

编辑:由于OP已使用有关其已定义Reads的附加信息更新了他的问题。

那里定义的Reads的问题是他们正在使用as[T]。调用as时,您试图强制给定的json路径键入T,否则将抛出异常。因此,一旦遇到第一个验证错误,就会引发异常,并且您将丢失所有后续错误。不过,您的用例相对简单,因此我认为采用外观更现代的Reads会更好。
import play.api.libs.json._
import play.api.libs.functional.syntax._

case class EventConfig(param: Int, period: Int, threshold: Int, toggle: Boolean)

object EventConfig {

    implicit val jsonReads: Reads[EventConfig] = (
        (__ \ ConfigEventAttrs.PARAM).read[Int] and
        (__ \ ConfigEventAttrs.PERIOD).read[Int] and
        (__ \ ConfigEventAttrs.THRESHOLD).read[Int] and
        (__ \ ConfigEventAttrs.TOGGLE).read[Boolean]
    )(EventConfig.apply _)

}

这更加紧凑,并且使用函数语法将所有验证错误累积到JsResult中,而不是引发异常。

编辑2:为了满足OP对其他apply方法的需求。

如果您使用JSON构建对象的参数与case类的参数不同,请定义一个用于JSON的函数Reads而不是EventConfig.apply。假设您的EventConfig在JSON中确实是这样的:
(time: Long, param: Int)

但是相反,您希望它像这样:
case class EventConfig(time: Date, param: Int)

定义一个函数,从原始参数创建一个EventConfig:
def buildConfig(time: Long, param: Int) = EventConfig(DateUtils.timeSecToDate(time), param)

然后在buildConfig中使用EventConfig.apply而不是Reads:
implicit val jsonReads: Reads[EventConfig] = (
    (__ \ "time").read[Long] and
    (__ \ "param").read[Int]
)(buildConfig _)

我简化了这个示例,但是buildConfig可以是任何返回EventConfig且参数与您要验证的JSON对象匹配的函数。

08-24 15:44