在 circe 中解码 case 类、String 或 Int

Som*_*ame 4 json scala circe

我使用一些 Rest API 响应包含一种“混合”字段的 json。混合我的意思是它可以采用不同类型的值。在我的情况下ObjectString并且Int是允许的。在Object本身由1Int和1 String

我需要解码的对象如下所示:

一世

{
   field1: 32,
   ...
   value: {
      id: 23,
      text: "text"
   }
}
Run Code Online (Sandbox Code Playgroud)

{
   field1: 32,
   ...
   value: 21
}
Run Code Online (Sandbox Code Playgroud)

{
   field1: 32,
   ...
   value: "value"
}
Run Code Online (Sandbox Code Playgroud)

如何处理circe中的此类对象?

Mat*_*zok 6

假设您的案例类是:

@JsonCodec(decodeOnly = true)
case class X(id: Int, text: String)
Run Code Online (Sandbox Code Playgroud)

然后我可以假设你的领域是这样的:

type Mixed = X Either Int Either String
Run Code Online (Sandbox Code Playgroud)

解码它可能看起来像:

implicit val mixedDecoder: Decoder[Mixed] = 
  Decoder[X].map[Mixed](x => Left(Left(x))) or Decoder[Int].map[Mixed](i => Left(Right(i))) or Decoder[String].map[Mixed](s => Right(s))
Run Code Online (Sandbox Code Playgroud)

Either如果您定义它们的组合方式,您可以派生编解码器:左胜、右胜或任何您喜欢的:

implicit def eitherDecode[L: Decoder, R: Decoder]: Decoder[L Either R] =
  Decoder[L].map[L Either R](Left(_)) or Decoder[R].map[L Either R](Right(_))
Run Code Online (Sandbox Code Playgroud)

或者,您可以创建自己的 ADT(密封特征 + 案例类),然后编写手写解码器以避免使用鉴别器字段。

最重要的是,您必须以某种方式表达您正在解码的类型中的多态性(以理智的方式 -Any不算数),然后提供一个解码器来解码它。然后你可以简单地使用它:

@JsonCodec(decodeOnly = true)
case class BigClass(field1: String, value: Mixed)
Run Code Online (Sandbox Code Playgroud)


Yuv*_*kov 6

类似于@SomeName创建的方法,但解码器不需要HCursor

sealed trait Value
object Value {
  final case class Values(id: Int, text: String) extends Value
  final case class IntValue(i: Int) extends Value
  final case class StringValue(s: String) extends Value

  implicit val valueDecoder: Decoder[Value] = Decoder[String]
    .map[Value](StringValue)
    .or(Decoder[Int].map[Value](IntValue))
    .or(Decoder.forProduct2("id", "text")(Values.apply).map[Value](identity))
}
Run Code Online (Sandbox Code Playgroud)

和封闭的对象:

final case class Example(field1: Int, value: Value)
object Example {
  implicit val exampDecoder: Decoder[Example] =
    Decoder.forProduct2("field1", "value")(Example.apply)
}
Run Code Online (Sandbox Code Playgroud)

运行:

import io.circe.Decoder
import io.circe.parser._

def main(args: Array[String]): Unit = {
  val fst =
    """
      |{
      |   "field1": 32,
      |   "value": {
      |      "id": 23,
      |      "text": "text"
      |   }
      |}""".stripMargin

  val snd =
    """
      |{
      |   "field1": 32,
      |   "value": 21
      |}
      |""".stripMargin

  val third =
    """{
      |   "field1": 32,
      |   "value": "value"
      |}
      |""".stripMargin

  println(decode[Example](fst))
  println(decode[Example](snd))
  println(decode[Example](third))
}
Run Code Online (Sandbox Code Playgroud)

结果:

Right(Example(32,Values(23,text)))
Right(Example(32,IntValue(21)))
Right(Example(32,StringValue(value)))
Run Code Online (Sandbox Code Playgroud)