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,针对不同的非法情况返回精准的错误信息,让调用者能快速定位问题。
测试案例参考
下面是几个典型的测试场景和结果:
- 合法场景:仅包含usLocationDuration
{ "username": "john_doe", "usLocationDuration": 120 }
解析成功,得到UserLocation("john_doe", Some(120), None)。
- 合法场景:仅包含nonUSLocationDuration
{ "username": "jane_smith", "nonUSLocationDuration": 60 }
解析成功,得到UserLocation("jane_smith", None, Some(60))。
- 非法场景:同时包含两个字段
{ "username": "bob_brown", "usLocationDuration": 30, "nonUSLocationDuration": 45 }
返回错误:JSON不能同时包含usLocationDuration和nonUSLocationDuration字段。
- 非法场景:两个字段都缺失
{ "username": "alice_wong" }
返回错误:JSON必须包含usLocationDuration或nonUSLocationDuration中的一个字段。
内容的提问来源于stack exchange,提问作者richs
相关产品推荐
相关产品推荐

