【问题标题】:Play framework scala json validation exception播放框架scala json验证异常
【发布时间】:2014-07-29 16:35:49
【问题描述】:

我正在尝试使用播放框架 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())))))

为什么会这样,我应该如何使用 validate 方法?

====== 更新

我正在使用这样的读取实现:

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)
      }
    }
  }

【问题讨论】:

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


    【解决方案1】:

    这不是使用validate 的正确方法。我认为文档没有尽可能地强调它的重要性,但在 here 的部分 Using Validation 中对此进行了解释。

    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.
       }
    )
    

    让我们来看看这个。 JsResult 上的 fold 签名如下:

    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)
    

    然后在您的Reads 中使用buildConfig 而不是EventConfig.apply:

    implicit val jsonReads: Reads[EventConfig] = (
        (__ \ "time").read[Long] and 
        (__ \ "param").read[Int]
    )(buildConfig _)
    

    我缩短了这个示例,但buildConfig 可以是返回EventConfig 并且参数与您尝试验证的 JSON 对象相匹配的任何函数。

    【讨论】:

    • 在我使用折叠方法的代码中,为了简单起见,我以这种方式编写。我只想打印结果,而不是 EventConfig。但是我仍然遇到这个异常。问题在于错误的读取实现,它会抛出 JsResultException。如果你使用折叠事件,它仍然会抛出这个异常,我将在更新中添加这个读取实现。谢谢你的解释。
    • @JMichal 我已经更新了我的答案以解决Reads。
    • 在我的“真实”代码中,我不得不在 apply 中使用额外的参数,这就是我使用这种构造的原因,或者你知道如何在这种语法中添加它们?
    • 您所说的“附加参数”是什么意思,它们与此有何不同?
    • EventConfig 有一个参数time,我将转换后的时间(以秒为单位)传递给 milis:DateUtils.timeSecToDate((json \ ConfigEventAttrs.TIME).as[Long])
    【解决方案2】:

    验证取决于您的 Reads 方法,我在那里遇到了问题。我应该在我的阅读中捕捉到这个异常。

    【讨论】:

    • :- 这应该作为评论添加,而不是答案
    • 但这是一个答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-05-03
    • 2013-07-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-16
    相关资源
    最近更新 更多