我使用一些 Rest API 响应包含一种“混合”字段的 json。混合我的意思是它可以采用不同类型的值。在我的情况下Object,String并且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中的此类对象?
假设您的案例类是:
@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)
类似于@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)