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

Scala中将JSON解析为泛型样例类时出现隐式发散错误

问题解决:泛型样例类的Play JSON解析异常

错误原因

你遇到的diverging implicit expansion错误,本质是Play JSON的自动推导机制无法处理带**高阶类型参数F[_]**的字段:

  1. 编译器不知道如何将JSON中的普通String值转换为F[String](比如F是Option、Either这类容器类型时,需要对应的Reads实例);
  2. 待解析的JSON缺少uid字段,即使F是Option这类支持空值的类型,也需要明确的处理逻辑。

解决方案

核心是给高阶类型F[_]定义转换规则,同时处理字段缺失的情况,以下是两种常见场景的实现:

场景1:F为Option(处理可选字段)

import play.api.libs.json._

// 泛型样例类定义
final case class LocationAPIObject[F[_]](
  countryCode: F[String],
  partyId: F[String],
  uid: F[String]
)

// 定义类型类,描述如何将JSON值转换为F[String]
trait ReadsF[F[_]] {
  def stringReads: Reads[F[String]]
}

// 为Option实现类型类:将缺失字段解析为None
implicit val optionReadsF: ReadsF[Option] = new ReadsF[Option] {
  override def stringReads: Reads[Option[String]] = Reads.optionWithNull(Reads.of[String])
}

// 为泛型样例类推导Reads,依赖ReadsF[F]的隐式实例
implicit def locationReads[F[_]](implicit rf: ReadsF[F]): Reads[LocationAPIObject[F]] = Json.reads[LocationAPIObject[F]]

// 测试解析
val js: JsValue = Json.parse("""{
  "countryCode": "us",
  "partyId": "123456"
}""")

// 解析结果为JsSuccess(LocationAPIObject(Some("us"), Some("123456"), None), ...)
val parseResult = js.validate[LocationAPIObject[Option]]

场景2:F为Id(即直接对应String,处理必填字段)

如果F是代表"无包装"的Id类型(type Id[A] = A),需要处理缺失的uid字段,比如给默认值:

import play.api.libs.json._

type Id[A] = A
final case class LocationAPIObject[F[_]](
  countryCode: F[String],
  partyId: F[String],
  uid: F[String]
)

// 为Id实现类型类:直接解析为String
implicit val idReadsF: ReadsF[Id] = new ReadsF[Id] {
  override def stringReads: Reads[String] = Reads.of[String]
}

// 自定义Reads,给uid设置默认值
implicit def locationReads[F[_]](implicit rf: ReadsF[F]): Reads[LocationAPIObject[F]] = (
  (__ \ "countryCode").read(rf.stringReads) and
  (__ \ "partyId").read(rf.stringReads) and
  // 若JSON无uid字段,使用默认值"unknown"
  (__ \ "uid").readNullable(rf.stringReads).map(_.getOrElse("unknown".asInstanceOf[F[String]]))
)(LocationAPIObject.apply[F])

// 测试解析
val js: JsValue = Json.parse("""{
  "countryCode": "us",
  "partyId": "123456"
}""")

// 解析结果为JsSuccess(LocationAPIObject("us", "123456", "unknown"), ...)
val parseResult = js.validate[LocationAPIObject[Id]]

关键说明

  • 必须通过**类型类(比如ReadsF)**约束F[_]的行为,让编译器知道如何处理F[String]的JSON转换;
  • 若JSON字段不全,需在Reads中明确处理逻辑(默认值、可选解析等),避免推导失败。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 01:01:11