Reputation: 1412
My goal is to transform JSON into the following model:
case class Container(typeId: Int, timestamp: Long, content: Content)
sealed trait Content
case class ContentType1(...) extends Content
case class ContentType2(...) extends Content
case class ContentType3(...) extends Content
content
of the container is represented by types that look totally different (regarding the amount and type of attributes). However, all content types are known at compile time and implement a sealed trait.typeId
attribute of the container indicates the content type. E.g. a value of N
means that content
is of type ContentTypeN
and so on.Container[A <: Content]
if that's a more elegant solution). What would be a good way to decode that with circe? I guess automatic decoding does not work in this case.
edit: the documentation of the json structure describes the content field as ?mixed (object, integer, bool)
, so it can also be a simple Int
or Boolean
instead of a case class object. But for now it would be ok to ignore these two types (although it would be nice to have a solution for that).
Upvotes: 4
Views: 1345
Reputation: 21730
I'd use semiauto (with deriveDecoder), validate and or.
case class Container[+C](typeId: Int, timestamp: Long, content: C)
sealed trait Content
@JsonCodec case class ContentType1(...) extends Content
@JsonCodec case class ContentType2(...) extends Content
object Content {
import shapeless._
import io.circe._
import io.circe.generic.semiauto._
def decodeContentWithGuard[T: Decoder](number: Int): Decoder[Content[T]] = {
deriveDecoder[Content[T]].validate(_.downField("typeId") == Right(number), s"wrong type number, expected $number")
}
implicit val contentDecoder: Decoder[Container[Content]] = {
decodeWithGuard[ContentType1](1) or
decodeWithGuard[ContentType2](2)
}
}
If you don't want the covariant annotation, you'll have to map and upcast the inner ContentTypeX
to Content
.
There's probably also a solution without the generic, but then you can't use semiauto.
Might not compile, didn't test it.
Upvotes: 0