如何在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
相关产品推荐
相关产品推荐

