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.

JSON encoding of case classes with the same name in different packages

See original GitHub issue

I’m working with a large collection of case classes that extend a sealed trait. In order to avoid naming collisions these case classes are grouped into different objects (they could also be grouped in packages).

For instance I have an object of type Tabs.OnCreated and another of type Windows.OnCreated. The JSON representation only contains the simple class name though, so it’s impossible to distinguish the two types. The decoder just creates an instance of the first type.

I know this is probably a very special case. Still I’m wondering if there’s a way to support that kind of structure in Circe. I guess we’d need something like the full class name as discriminator in the JSON representation.

Minimal example below:

import io.circe.parser.decode
import io.circe.syntax._
import io.circe.generic.auto._

sealed trait Event
object Tabs { // could also be package tabs, same result
  case class OnCreated(id: String) extends Event
}
object Windows {
  case class OnCreated(id: String) extends Event
}

object CirceEval extends App {
  val tabOnCreated: Event = Tabs.OnCreated("tab created")
  val tabOnCreatedJson = tabOnCreated.asJson.toString()
  val decodedTabEvent = decode[Event](tabOnCreatedJson)
  println(tabOnCreatedJson)
  println(decodedTabEvent)
  println(decodedTabEvent.map(_.getClass))

  val windowOnCreated: Event = Windows.OnCreated("window created")
  val windowOnCreatedJson = windowOnCreated.asJson.toString()
  val decodedWindowEvent = decode[Event](windowOnCreatedJson)
  println(windowOnCreatedJson)
  println(decodedWindowEvent)
  println(decodedWindowEvent.map(_.getClass))
}

Output:

{
  "OnCreated" : {
    "id" : "tab created"
  }
}
Right(OnCreated(tab created))
Right(class CirceEval$Tabs$OnCreated)

{
  "OnCreated" : {
    "id" : "window created"
  }
}
Right(OnCreated(window created))
Right(class CirceEval$Tabs$OnCreated)

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Comments:6 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
travisbrowncommented, Jan 26, 2017

I’d be inclined not to provide special support for this case, since Shapeless’s generic representation doesn’t include the necessary information, and since there are potentially lots of ways the discriminator key could be chosen. Writing explicit instances for the top level isn’t too bad:


import cats.syntax.functor._
import io.circe.{ Decoder, Encoder, Json }
import io.circe.generic.auto._
import io.circe.parser.decode
import io.circe.syntax._

sealed trait Event

object Tabs {
  case class OnCreated(id: String) extends Event
}

object Windows {
  case class OnCreated(id: String) extends Event
}

object Event {
  implicit val encodeEvent: Encoder[Event] = Encoder.instance {
    case e @ Tabs.OnCreated(_)    => Json.obj("Tabs.OnCreated"    -> e.asJson)
    case e @ Windows.OnCreated(_) => Json.obj("Windows.OnCreated" -> e.asJson)
  }

  private val decodeT = Decoder[Tabs.OnCreated].prepare(_.downField("Tabs.OnCreated"))
  private val decodeW = Decoder[Windows.OnCreated].prepare(_.downField("Windows.OnCreated"))

  implicit val decodeEvent: Decoder[Event] = decodeT.or(decodeW.widen[Event])
}

What do you think?

0reactions
travisbrowncommented, Jun 26, 2017

I’m going to go ahead and close this since I don’t want to stray too far from Shapeless’s generic representation for the basic case, and since master now supports transforming the constructor name via implicit configuration after #648 / #684.

Read more comments on GitHub >

github_iconTop Results From Across the Web

How can I configure Circe to stop using nested class names ...
How can I configure Circe to stop using nested class names as key names in encoded JSON? ... I'm trying to encode a...
Read more >
Encoding and Decoding Custom Types - Apple Developer
Encoding and Decoding Custom Types. Make your data types encodable and decodable for compatibility with external representations such as JSON. Overview.
Read more >
JSON Handling - http4s
JSON Handling · Add the JSON support module(s) · Sending Raw JSON · Encoding case classes as JSON · Receiving Raw...
Read more >
39 JSON in Oracle Database
By definition, textual JSON data is encoded using a Unicode encoding, either UTF-8 or UTF-16. You can use textual data that is stored...
Read more >
How to serialize properties of derived classes with System ...
Learn how to serialize polymorphic objects while serializing to and deserializing from JSON in .NET.
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