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

如何在circe中解码含混合类型字段的Rest API响应JSON?

在Circe中处理混合类型JSON字段

这个场景在对接REST API时特别常见——JSON里某个字段会返回多种类型的值,Circe默认没法自动处理这种情况,但咱们可以通过自定义解码器结合**代数数据类型(ADT)**来优雅解决。下面一步步来实现:

1. 定义对应Scala类型

首先用密封特质(sealed trait)定义Value类型,清晰覆盖三种可能的取值情况,后续处理时也能通过模式匹配安全区分:

// 密封特质确保所有子类都在当前作用域,避免遗漏模式匹配
sealed trait Value
// 对应JSON中Object类型的value字段
case class ValueObject(id: Int, text: String) extends Value
// 对应JSON中Int类型的value字段
case class ValueInt(value: Int) extends Value
// 对应JSON中String类型的value字段
case class ValueString(value: String) extends Value

// 主对象类型,包含示例中的field1和混合类型的value
case class MyObject(field1: Int, value: Value)

2. 自定义Circe解码器

Circe的解码器支持用orElse链式尝试不同的解码逻辑,咱们先为ValueObject生成自动解码器,再依次尝试解码成Object、Int、String,并映射到对应的Value子类:

import io.circe._
import io.circe.generic.semiauto._

// 为ValueObject生成自动解码器(利用circe的自动推导)
implicit val valueObjectDecoder: Decoder[ValueObject] = deriveDecoder[ValueObject]

// 自定义Value的解码器:先尝试解码成Object,失败再试Int,最后试String
implicit val valueDecoder: Decoder[Value] = 
  valueObjectDecoder.map(ValueObject.apply)
    .orElse(Decoder[Int].map(ValueInt))
    .orElse(Decoder[String].map(ValueString))

// 为主对象生成解码器,会自动复用上面定义的valueDecoder
implicit val myObjectDecoder: Decoder[MyObject] = deriveDecoder[MyObject]

3. 测试解码逻辑

现在用你给出的三个示例JSON来验证:

  • 解码Object类型的value:
val json1 = """{"field1": 32, "value": {"id": 23, "text": "text"}}"""
val result1 = io.circe.parser.decode[MyObject](json1)
// 结果:Right(MyObject(32, ValueObject(23, "text")))
  • 解码Int类型的value:
val json2 = """{"field1": 32, "value": 21}"""
val result2 = io.circe.parser.decode[MyObject](json2)
// 结果:Right(MyObject(32, ValueInt(21)))
  • 解码String类型的value:
val json3 = """{"field1": 32, "value": "value"}"""
val result3 = io.circe.parser.decode[MyObject](json3)
// 结果:Right(MyObject(32, ValueString("value")))

4. (可选)自定义编码器

如果需要把MyObject编码回原始格式的JSON,同样可以自定义编码器,通过模式匹配处理不同的Value子类:

// 为ValueObject生成自动编码器
implicit val valueObjectEncoder: Encoder[ValueObject] = deriveEncoder[ValueObject]

// 自定义Value的编码器
implicit val valueEncoder: Encoder[Value] = Encoder.instance {
  case obj: ValueObject => valueObjectEncoder(obj)
  case ValueInt(i) => Encoder[Int].apply(i)
  case ValueString(s) => Encoder[String].apply(s)
}

// 主对象的编码器
implicit val myObjectEncoder: Encoder[MyObject] = deriveEncoder[MyObject]

编码后的JSON会和原始输入的格式完全一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 19:47:54