I'm having an issue with json parsing when field can have different primitive value types. For example, I can get json:
{
"name" : "john",
"age" : 31
}
Or it can be in this form:
{
"name" : "john",
"age" : "thirty one"
}
Or in this way:
{
"name" : "john",
"age" : 31.0
}
I want to be able to parse field age
to the following ADTs instances:
sealed trait PrimitiveWrapper
case class IntWrapper(v: Int) extends PrimitiveWrapper
case class StringWrapper(v: String) extends PrimitiveWrapper
case class FloatWrapper(v: Float) extends PrimitiveWrapper
So at the end I can get something like this:
case class Person(name: String, age: PrimitiveWrapper)
How can I do this? I found this topic: How to decode an ADT with circe without disambiguating objects
But in that solution we are parsing not primitive fields.
This is how you can do:
import cats.syntax.functor._
import io.circe.Decoder, io.circe.generic.auto._
sealed trait PrimitiveWrapper
case class IntWrapper(v: Int) extends PrimitiveWrapper
case class StringWrapper(v: String) extends PrimitiveWrapper
case class FloatWrapper(v: Float) extends PrimitiveWrapper
case class Person(name: String, age: PrimitiveWrapper)
object GenericDerivation {
implicit val decodePrimitiveWrapper: Decoder[PrimitiveWrapper] =
List[Decoder[PrimitiveWrapper]](
Decoder.decodeInt.map(IntWrapper).widen,
Decoder.decodeString.map(StringWrapper).widen,
Decoder.decodeFloat.map(FloatWrapper).widen
).reduceLeft(_ or _)
def main(args: Array[String]): Unit = {
import io.circe.parser.decode
println(decode[Person]("""{"name" : "john", "age" : 31 }"""))
println(decode[Person]("""{"name" : "john", "age" : "thirty one" }"""))
println(decode[Person]("""{"name" : "john", "age" : 31.3 }"""))
// Prints
// Right(Person(john,IntWrapper(31)))
// Right(Person(john,StringWrapper(thirty one)))
// Right(Person(john,FloatWrapper(31.3)))
}
}
Note: The following get parse using an IntWrapper
println(decode[Person]("""{"name" : "john", "age" : 31.0 }"""))
Update: As @Travis pointed out the decodePrimitiveWrapper
can be written like this:
implicit val decodePrimitiveWrapper: Decoder[PrimitiveWrapper] =
Decoder.decodeInt.map(IntWrapper).widen[PrimitiveWrapper] or
Decoder.decodeString.map(StringWrapper).widen[PrimitiveWrapper] or
Decoder.decodeFloat.map(FloatWrapper).widen[PrimitiveWrapper]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With