play-json-derived-codecs icon indicating copy to clipboard operation
play-json-derived-codecs copied to clipboard

Include discriminator when serializing concrete type

Open sam-veezoo opened this issue 1 year ago • 1 comments
trafficstars

Is there a way to include the discriminator even when serializing the concrete type? Concretely, I'd like to instantiate the library in this piece of code and get the same output in both println statements:

sealed trait Base
case class Foo(name: String) extends Base

object Test {
  def test(): Unit = {
    val foo = Foo("Hello")
    val fooAsBase: Base = foo

    println(Json.toJson(fooAsBase).toString())    // expected: { "type": "Foo", "name": "Hello" }
    println(Json.toJson(foo).toString())          // expected: { "type": "Foo", "name": "Hello" }
  }
}

The ideomatic way to instantiate the library would only provide us with Writes[Base] used in the first println statement. I attempted to use generics in order to obtain suitable implicits for Writes[Foo] and Writes[Base] as follows:

object Base {
  implicit def writes[T <: Base]: Writes[T] =
    julienrf.json.derived.flat.owrites[Base](
      (__ \ "type").write[String]
    ).asInstanceOf[Writes[T]]
}

This used to work in play-json-derived-codes version 7.0.0 (together with play-json_2.12:2.8.2), however I end up with infinite recursion when using a more recent play-json-derived-codes version 10.1.0 (together with play-json_2.13:2.10.5):

at julienrf.json.derived.DerivedOWritesUtil$$anon$6.$anonfun$owrites$6(DerivedOWrites.scala:130)
at play.api.libs.json.OWrites$$anon$4.writes(Writes.scala:150)
at play.api.libs.json.OWrites.$anonfun$contramap$2(Writes.scala:76)
at play.api.libs.json.OWrites$$anon$4.writes(Writes.scala:150)
at play.api.libs.json.OWrites$$anon$4.writes(Writes.scala:149)
at julienrf.json.derived.TypeTagOWrites$$anon$2.$anonfun$owrites$2(typetags.scala:91)
at play.api.libs.json.OWrites$$anon$4.writes(Writes.scala:150)
at julienrf.json.derived.DerivedOWritesUtil$$anon$6.$anonfun$owrites$6(DerivedOWrites.scala:130)
...

Given my attempt is quite acrobatic, I am not sure whether the infinite recursion is actually a bug in the library.

Is there a way to achieve the expected result and obtain the same JSON serialization for the sealed trait as well as for the concrete case class?

sam-veezoo avatar Jun 07 '24 15:06 sam-veezoo