解码 case class、String 或 Int in circe
Decode case class, String or Int in circe
我使用一些 Rest API 响应 json 包含一种 "mixed" 字段。混合是指它可以采用不同类型的值。在我的例子中,Object
、String
和 Int
是允许的。 Object
本身由 1 Int
和 1 String
.
组成
我需要解码的对象如下所示:
我
{
field1: 32,
...
value: {
id: 23,
text: "text"
}
}
二
{
field1: 32,
...
value: 21
}
三
{
field1: 32,
...
value: "value"
}
周围有这样的物体怎么处理?
假设您的情况 class 是:
@JsonCodec(decodeOnly = true)
case class X(id: Int, text: String)
那么我可以假设您的字段类型为:
type Mixed = X Either Int Either String
解码如下:
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))
如果您定义它们的组合方式,您可以为 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(_))
或者,您可以创建自己的 ADT(密封特征 + 大小写 classes),然后编写手写解码器以避免使用鉴别器字段。
底线是你必须以某种方式表达你正在解码的类型中的多态性(以一种理智的方式 - Any
不算数)然后提供一个解码器来解码它.然后你可以简单地使用它:
@JsonCodec(decodeOnly = true)
case class BigClass(field1: String, value: Mixed)
在查看@MateuszKubuszok 提供的答案之前,我最终编写了一个自定义解码器。为了完整起见,我会把它放在这里。
sealed trait SomeValue
final case class SomeObjValue(id: Int, text: String) extends SomeValue
final case class SomeIntValue(int: Int) extends SomeValue
final case class SomeStringValue(str: String) extends SomeValue
implicit def someValueDecode: Decoder[SomeValue] =
(cursor: HCursor) =>
if (cursor.value.isObject) Decoder[SomeObjValue].apply(cursor)
else if (cursor.value.isString) cursor.value.as[String].map(SomeStringValue)
else if (cursor.value.isNumber) cursor.value.as[Int].map(SomeIntValue)
else
Decoder.resultInstance.raiseError(
DecodingFailure(s"${cursor.value} is not supported for decoding", List())
)
与 创建的方法类似,但解码器不需要 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))
}
和封闭对象:
final case class Example(field1: Int, value: Value)
object Example {
implicit val exampDecoder: Decoder[Example] =
Decoder.forProduct2("field1", "value")(Example.apply)
}
运行它:
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))
}
结果:
Right(Example(32,Values(23,text)))
Right(Example(32,IntValue(21)))
Right(Example(32,StringValue(value)))
我使用一些 Rest API 响应 json 包含一种 "mixed" 字段。混合是指它可以采用不同类型的值。在我的例子中,Object
、String
和 Int
是允许的。 Object
本身由 1 Int
和 1 String
.
我需要解码的对象如下所示:
我
{
field1: 32,
...
value: {
id: 23,
text: "text"
}
}
二
{
field1: 32,
...
value: 21
}
三
{
field1: 32,
...
value: "value"
}
周围有这样的物体怎么处理?
假设您的情况 class 是:
@JsonCodec(decodeOnly = true)
case class X(id: Int, text: String)
那么我可以假设您的字段类型为:
type Mixed = X Either Int Either String
解码如下:
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))
如果您定义它们的组合方式,您可以为 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(_))
或者,您可以创建自己的 ADT(密封特征 + 大小写 classes),然后编写手写解码器以避免使用鉴别器字段。
底线是你必须以某种方式表达你正在解码的类型中的多态性(以一种理智的方式 - Any
不算数)然后提供一个解码器来解码它.然后你可以简单地使用它:
@JsonCodec(decodeOnly = true)
case class BigClass(field1: String, value: Mixed)
在查看@MateuszKubuszok 提供的答案之前,我最终编写了一个自定义解码器。为了完整起见,我会把它放在这里。
sealed trait SomeValue
final case class SomeObjValue(id: Int, text: String) extends SomeValue
final case class SomeIntValue(int: Int) extends SomeValue
final case class SomeStringValue(str: String) extends SomeValue
implicit def someValueDecode: Decoder[SomeValue] =
(cursor: HCursor) =>
if (cursor.value.isObject) Decoder[SomeObjValue].apply(cursor)
else if (cursor.value.isString) cursor.value.as[String].map(SomeStringValue)
else if (cursor.value.isNumber) cursor.value.as[Int].map(SomeIntValue)
else
Decoder.resultInstance.raiseError(
DecodingFailure(s"${cursor.value} is not supported for decoding", List())
)
与 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))
}
和封闭对象:
final case class Example(field1: Int, value: Value)
object Example {
implicit val exampDecoder: Decoder[Example] =
Decoder.forProduct2("field1", "value")(Example.apply)
}
运行它:
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))
}
结果:
Right(Example(32,Values(23,text)))
Right(Example(32,IntValue(21)))
Right(Example(32,StringValue(value)))