Search code examples
jsonscalacase-classcirce

How can I configure Circe to stop using nested class names as key names in encoded JSON?


I'm trying to encode a case class (where some properties are also case classes), and I'm getting the nested case class name as the key name in the JSON. Is there a simple way to avoid that without creating a custom encoder? The nested classes inherit from a sealed trait.

I'm currently using semi-automatic derivation.

The following worksheet example shows my problem:

case class A(foo: Int, bar: Sub)

sealed trait Sub
case class B(x: Int, y: Int) extends Sub
case class C(x: Int, y: Int, z: Int) extends Sub

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

implicit val bEncoder: Encoder[Sub] = deriveEncoder
implicit val aEncoder: Encoder[A] = deriveEncoder

A(123, B(8, 8)).asJson
A(456, C(8, 8, 8)).asJson

Instead of getting:

res0: io.circe.Json = {
  "foo" : 123,
  "bar" : {
    "x" : 8,
    "y" : 8
  }
}
res1: io.circe.Json = {
  "foo" : 456,
  "bar" : {
    "x" : 8,
    "y" : 8,
    "z" : 8
  }
}

I'm getting

res0: io.circe.Json = {
  "foo" : 123,
  "bar" : {
    "B" : {
      "x" : 8,
      "y" : 8
    }
  }
}
res1: io.circe.Json = {
  "foo" : 456,
  "bar" : {
    "C" : {
      "x" : 8,
      "y" : 8,
      "z" : 8
    }
  }
}

Solution

  • I ended up just creating my own encoder. The other option was to define a discriminator which could take the class name and set it as a property in the object, but I needed to create specific JSON without any additional properties. If somebody else comes along and wants to do this and doesn't mind having a 'type' property, the take a look at the Configuration class.

    import io.circe.generic.extras.semiauto._
    implicit val configuration: Configuration = 
      Configuration.default.withDiscriminator("type")
    implicit val json: Encoder[FooBar] = deriveEncoder[FooBar]
    

    Take note of the semiauto import from the extras package.