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

Playframework [Scala]:JSON解析需强制二选一字段(不可同时/都缺失)

实现Play JSON的二选一字段强制校验

没问题!针对你这个「两个可选字段必须二选一」的校验需求,咱们可以利用Play JSON的自定义Reads和校验逻辑来实现,既保证JSON解析时的规则校验,也能防止直接构造不符合要求的实例。下面是完整的实现代码和思路拆解:

完整实现代码

import play.api.libs.functional.syntax._
import play.api.libs.json._

final case class UserLocation(
  username: String,
  usLocationDuration: Option[Int],
  nonUSLocationDuration: Option[Int]
)

object UserLocation {
  // 自定义JSON级互斥校验:必须且仅存在两个时长字段中的一个
  private val exclusiveDurationCheck: Reads[Unit] = Reads[Unit] { json =>
    val hasUsField = (json \ "usLocationDuration").toOption.isDefined
    val hasNonUsField = (json \ "nonUSLocationDuration").toOption.isDefined
    
    (hasUsField, hasNonUsField) match {
      case (true, false) | (false, true) => JsSuccess(())
      case (true, true) => JsError("JSON不能同时包含usLocationDuration和nonUSLocationDuration字段")
      case (false, false) => JsError("JSON必须包含usLocationDuration或nonUSLocationDuration中的一个字段")
    }
  }

  implicit val userLocationReads: Reads[UserLocation] = (
    (__ \ "username").read[String] and
    (__ \ "usLocationDuration").readNullable[Int] and
    (__ \ "nonUSLocationDuration").readNullable[Int]
  )(UserLocation.apply _)
  // 校验构造后的实例,防止直接通过构造器创建不符合规则的对象
  .filter(JsonValidationError("必须且仅存在一个时长字段")) { location =>
    val hasUs = location.usLocationDuration.isDefined
    val hasNonUs = location.nonUSLocationDuration.isDefined
    (hasUs && !hasNonUs) || (!hasUs && hasNonUs)
  }
  // 追加JSON结构的精准校验,给出明确错误提示
  .andThen(exclusiveDurationCheck)
}

实现思路拆解

  • 基础字段读取:先用readNullable[Int]读取两个可选字段,完成基础的类型校验和字段读取。
  • 实例级校验:通过filter方法校验构造后的UserLocation实例,确保即使有人直接调用UserLocation构造器,也无法创建不符合规则的对象。
  • JSON结构校验:单独定义exclusiveDurationCheck直接校验原始JSON,针对不同的非法情况返回精准的错误信息,让调用者能快速定位问题。

测试案例参考

下面是几个典型的测试场景和结果:

  1. 合法场景:仅包含usLocationDuration
{
  "username": "john_doe",
  "usLocationDuration": 120
}

解析成功,得到UserLocation("john_doe", Some(120), None)。

  1. 合法场景:仅包含nonUSLocationDuration
{
  "username": "jane_smith",
  "nonUSLocationDuration": 60
}

解析成功,得到UserLocation("jane_smith", None, Some(60))。

  1. 非法场景:同时包含两个字段
{
  "username": "bob_brown",
  "usLocationDuration": 30,
  "nonUSLocationDuration": 45
}

返回错误:JSON不能同时包含usLocationDuration和nonUSLocationDuration字段。

  1. 非法场景:两个字段都缺失
{
  "username": "alice_wong"
}

返回错误:JSON必须包含usLocationDuration或nonUSLocationDuration中的一个字段。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 06:43:09