Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
The JSON tagger allows to decode an ADT encoded in JSON avoiding the focus to switch to a different branch
import io.circe._
import io.circe.generic.semiauto._
import cats.syntax.all._
object JsonTagger {
private implicit class Tagger[A](d: Decoder[A]) {
def tag(accessor: String): Decoder[Decoder[A]] =
Decoder
.instance { inputJson =>
inputJson.downField(accessor) match {
case err: FailedCursor => Left(DecodingFailure(s"key $accessor expected", err.history))
case innerJson: HCursor if innerJson.value.isNull =>
Left(DecodingFailure(s"key $accessor expected to be not null", innerJson.history))
case innerJson: HCursor => Right(innerJson)
}
}
.map(outJson => Decoder.instance(_ => d(outJson)))
}
sealed trait Animal
object Animal {
case class Dog(name: String, tailLength: Int) extends Animal
case class Cat(name: String, preferredMilk: String) extends Animal
}
/**
* This decoder does not return an useful message, because it will always return the failure from the `decodeDog` even if the JSON is for a cat.
*
* {{{
* {
* "animal": {
* "cat": {
* "name": "Toby"
* }
* }
* }
* }}}
*
* This JSON snippet will fail for something like `dog` is an ivalid cursor.
*/
val buggedDecoderForAnimal: Decoder[Animal] = {
val decodeCat = deriveDecoder[Cat].prepare(hc => hc.downField("cat"))
val decodeDog = deriveDecoder[Dog].prepare(hc => hc.downField("dog"))
decodeCat.orElse(decodeDog)
}
/**
* This decoder returns an useful message, because it will not fallback to the other decoder if "cat" or "dog" keys exist.
*
* {{{
* {
* "animal": {
* "cat": {
* "name": "Toby"
* }
* }
* }
* }}}
*
* This JSON snippet will fail for something like `preferredMilk` is null or an ivalid cursor. So it does not fallback to "dog"
. */
val taggedDecoderForAnimal: Decoder[Animal] = {
val decodeCat = deriveDecoder[Cat].tag("cat")
val decodeDog = deriveDecoder[Dog].tag("dog")
decodeCat.orElse(decodeDog)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment