You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Play框架JSON转case class时Extractor未按预期抛出异常求助

Play框架JSON转case class未触发自定义异常的问题解决

核心原因

Play JSON 提供的 Json.reads[CustomerPublicForm] 宏生成的反序列化逻辑,不会调用伴生对象中你自定义的 apply 方法。
该宏在编译期会直接解析case class的主构造器参数,生成的反序列化代码会直接读取JSON对应字段的值,直接构造case class实例,完全绕开了你写在自定义apply中的校验逻辑,因此你抛出的"John Exception"永远没有执行机会,反序列化会直接返回JsSuccess,自然不会进入JsError分支。

解决方案

你可以选择以下任意一种方式将校验逻辑嵌入反序列化流程:

方案1:手动构造Reads嵌入校验

放弃自动生成的Json.reads,手动定义Reads并使用Play JSON原生的校验算子,校验失败会直接返回JsError,符合你的预期:

implicit val readsPublicLeadFormRequest: Reads[CustomerPublicForm] = (
  (__ \ "firstName").read[String]
    .filter(JsonValidationError("John Exception"))(!_.equalsIgnoreCase("John")) and
  (__ \ "lastName").read[String]
)(CustomerPublicForm.apply _)

方案2:自动生成Reads后追加校验

如果你不想完全手写Reads,可以在自动生成的Reads基础上追加后置校验:

implicit val readsPublicLeadFormRequest: Reads[CustomerPublicForm] = 
  Json.reads[CustomerPublicForm].filter(JsonValidationError("John Exception"))(
    form => !form.firstName.equalsIgnoreCase("John")
  )

方案3:直接调用自定义apply并捕获异常

如果你必须复用自定义apply中的逻辑,可以通过flatMap调用apply,同时将异常转换为JsError:

implicit val readsPublicLeadFormRequest: Reads[CustomerPublicForm] = (
  (__ \ "firstName").read[String] and
  (__ \ "lastName").read[String]
).tupled.flatMap { case (firstName, lastName) =>
  Reads(_ =>
    try JsSuccess(CustomerPublicForm(firstName, lastName))
    catch { case e: Exception => JsError(e.getMessage) }
  )
}

内容的提问来源于stack exchange,提问作者glarkou

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 01:24:05