question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

JsonCodec for sealed traits requires an explicit object definition

See original GitHub issue

As reported by @ngbinh, the following does not compile in circe 0.4.0-RC1:

import io.circe.generic.JsonCodec

@JsonCodec sealed trait A
case class B(b: String) extends A
case class C(c: Int) extends A

The expanded version fails as well:

import io.circe.{ Decoder, Encoder }

sealed trait A

object A {
  implicit val encodeA: Encoder[A] = io.circe.generic.semiauto.deriveEncoder[A]
  implicit val decodeA: Decoder[A] = io.circe.generic.semiauto.deriveDecoder[A]
}

case class B(b: String) extends A
case class C(c: Int) extends A

But can be fixed by moving the object A definition after the case class definitions.

Similarly, it’s possible to work around the issue with @JsonCodec by adding an object definition (potentially empty) after the case classes:

import io.circe.generic.JsonCodec

@JsonCodec sealed trait A
case class B(b: String) extends A
case class C(c: Int) extends A

object A

This isn’t that terrible, but it’s an annoying thing to have to remember. I’m not sure we can fix the JsonCodec macro annotation so that this workaround isn’t necessary, but we should at least take a look (probably after the 0.4.0 release).

Issue Analytics

  • State:open
  • Created 7 years ago
  • Comments:20 (13 by maintainers)

github_iconTop GitHub Comments

6reactions
travisbrowncommented, Apr 15, 2016

The exact static type always determines which instance will be used, and circe-generic will always give different instances for A and B here.

If you wanted instances for A that didn’t use the object wrapper, you could do something this:

import io.circe._, io.circe.generic.JsonCodec, io.circe.syntax._

sealed trait A
@JsonCodec case class B(b: String) extends A
@JsonCodec case class C(c: Int) extends A
object A {
  implicit val decodeA: Decoder[A] = Decoder[B].map[A](identity).or(Decoder[C].map[A](identity))
  implicit val encodeA: Encoder[A] = Encoder.instance {
    case b @ B(_) => b.asJson
    case c @ C(_) => c.asJson
  }
}

And then:

scala> (B("abc"): A).asJson.noSpaces
res0: String = {"b":"abc"}

scala> B("abc").asJson.noSpaces
res1: String = {"b":"abc"}

This isn’t the default because in some cases it can lead to ambiguity in decoding, but if your case class member names don’t overlap it can be a reasonable thing to do.

2reactions
nafgcommented, Sep 8, 2017

Is it supposed to work if I nest the case classes inside the sealed trait’s companion?

@JsonCodec
sealed trait A

object A {
  case class A1(x: String) extends A
  case class A2(y: Int) extends A
}
Read more comments on GitHub >

github_iconTop Results From Across the Web

JsonCodec for sealed traits requires an explicit object definition
JsonCodec for sealed traits requires an explicit object definition. ... JsonCodec @JsonCodec sealed trait A case class B(b: String) extends A case class ......
Read more >
Circe Decode to sealed trait extended by multiple case classes
_ object CirceSubtypesSerialisers { def main(args: ... and JsonCodec for sealed traits requires an explicit object definition.
Read more >
circe/circe - Gitter
alternately if you're explicitly passing encode rather than using implicit ... sealed trait MyType object MyType { case object SomeType extends MyType case ......
Read more >
Warnings and known issues - circe
Generic derivation may not work as expected when the type definitions that you're ... _ scala> sealed trait A; case object B extends...
Read more >
Introduction to Traits in Scala - Baeldung
A musical score needs to have a composition. Let's begin by creating a Composition trait: trait Composition { var composer: String def ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found