我正在尝试使用播放框架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
,其中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构建对象的参数与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对象匹配的函数。