播放框架scala json验证异常

时间:2014-06-09 06:59:15

标签: json scala playframework-2.0 actor playframework-2.2

我试图使用play framework 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)
      }
    }
  }

2 个答案:

答案 0 :(得分:14)

这不是使用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.
   }
)

让我们稍微研究一下。 foldJsResult的签名如下:

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,其中A类型JsResult已经验证为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构建对象的参数与案例类的参数不同,请定义用于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对象的参数匹配。

答案 1 :(得分:0)

验证取决于您的Reads方法,我在那里遇到了问题。我应该在阅读中抓住这个例外。