使用play-json-derived-codecs无法序列化键为密封特质的Map问题
问题成因
Play JSON 对 Map[K, V] 类型的序列化逻辑有特殊要求:JSON 规范中对象的键只能是字符串类型,因此 Play JSON 默认只为键类型存在 KeyFormat 隐式实例的 Map 提供 Format 实现。
你使用 play-json-derived-codecs 的 derived.oformat[Baz]() 生成的是 OFormat[Baz] 实例,该实例仅支持将 Baz 类型作为JSON值(比如普通的对象、字符串等字段值)序列化,不满足 Map 键需要的 KeyFormat 约束。当你把键类型改为 String 时运行正常,是因为 Play JSON 已经默认提供了 String 类型的 KeyFormat 隐式实现。
解决方案
有两种常见的修复方式:
方案1:为Baz实现自定义KeyFormat
在 Baz 的伴生对象中新增 KeyFormat[Baz] 隐式实例,定义 Baz 和字符串之间的互转逻辑即可:
sealed trait Baz object Baz { case object A extends Baz implicit val format: OFormat[Baz] = derived.oformat[Baz]() // 新增KeyFormat实现 implicit val bazKeyFormat: KeyFormat[Baz] = new KeyFormat[Baz] { override def read(key: String): JsResult[Baz] = key match { case "A" => JsSuccess(Baz.A) case other => JsError(s"非法的Baz键值: $other") } override def write(baz: Baz): String = baz match { case Baz.A => "A" } } } final case class Foo(s: Map[Baz, String]) object Foo { implicit val format: OFormat[Foo] = Json.format[Foo] }
方案2:调整存储结构规避Map键约束
如果不想额外实现 KeyFormat,可以将 Foo 中的 Map 类型替换为 Seq[(Baz, String)],用键值对数组的形式存储数据,此时 Baz 作为JSON值序列化,仅需要已有的 OFormat 实例即可正常工作:
final case class Foo(s: Seq[(Baz, String)]) { // 业务需要使用Map时可通过该方法转换 def sAsMap: Map[Baz, String] = s.toMap } object Foo { implicit val format: OFormat[Foo] = Json.format[Foo] }
内容的提问来源于stack exchange,提问作者Mojo
相关产品推荐
相关产品推荐

