zio-json
zio-json copied to clipboard
seeing error "magnolia: could not find any direct subtypes of trait" when its not true!
The pattern seems to be when the base sealed trait has several parameters
eg
sealed trait Foo[A]
but the child class instantiates them
case class Bar(..) extends Foo[SomeConcreteType]
i'm hoping its other stuff that spuriously confusing zio-json, because overall i'm finding it to be a very humane tool! (though i did have to roll my own Map[k,v] instance to get some stuff to work )
@cartazio I’m also wondering what the issue with Map[K, V]
was that you ran into
What was A
that you tried to derive for?
a colleague and I will try to boil down a working reproducer that you can run yourself.
we're currently using the 0.1 release, if we can get a reproducer into your hands would that be helpful?
import zio.json._
object Example {
trait Bar[T] {
def get(a: T): T
}
case class BarInstance() extends Bar[Int] {
def get(a: Int): Int = a
}
case class FooTwoInstance[A <: Bar[Int]](
`type`: Int
) extends FooTwo[A]
case class FooInstance (
fooTwo: FooTwo[BarInstance]
) extends Foo[BarInstance]
sealed trait FooTwo[A <: Bar[Int]] {
val `type`: Int
}
sealed trait Foo[A <: Bar[Int]] {
val fooTwo: FooTwo[A]
}
implicit def decoder[A <: Bar[Int]](): JsonDecoder[Foo[A]] =
DeriveJsonDecoder.gen[Foo[A]]
implicit def encoder[A <: Bar[Int]](): JsonEncoder[Foo[A]] =
DeriveJsonEncoder.gen[Foo[A]]
}
produces the following error:
magnolia: could not find any direct subtypes of trait Foo
DeriveJsonDecoder.gen[Foo[A]]
And if we modify the decoder and encoder to no longer use type parameters like so:
implicit def decoder(): JsonDecoder[Foo[BarInstance]] =
DeriveJsonDecoder.gen[Foo[BarInstance]]
implicit def encoder(): JsonEncoder[Foo[BarInstance]] =
DeriveJsonEncoder.gen[Foo[BarInstance]]
we get the following error:
magnolia: could not find JsonDecoder.Typeclass for type Example.FooTwo[Example.BarInstance]
in parameter 'fooTwo' of product type Example.FooInstance
in coproduct type Example.Foo[Example.BarInstance]
DeriveJsonDecoder.gen[Foo[BarInstance]]
thanks @agascon9 (my lovely colleague). this gives the essence of our problem (though it happens a few different ways).
and it actually nicely shows that the remaining challenges we have in our effort to migrate a code base to the joys of zio-json
are one and the same (trying to fix our errors one way, pushes us to have errors another way ).
Any further ways we can help test/facilitate/validate a fix and subsequent release, we are at your service to enable and make happen. (the code base in question is large enough that moving to zio-json will transform the dev experience/build time for all our contributors, and help us generally do nice things)
I guess we could hand write those instances as our near term fix, but i'm a tad leary of that :)
Thanks for the minimisation, I assume this is rather a problem with magnolia itself. I can't recall how well it supports GADTs.
yeah, once i realized it was a funny scala gadt i was worried about that. At the very least, better errormessages about how to resolve it would be great. I think our fix ultimately is to just do the equivalent of DeriveJsonEncoder.gen[Foo[BarInstance]]
sortah
Currently jsoniter-scala-macros doesn't derive codecs for generic types, but it works fine for non-generic types:
import com.github.plokhotnyuk.jsoniter_scala.macros._
import com.github.plokhotnyuk.jsoniter_scala.core._
object Example {
trait Bar[T] {
def get(a: T): T
}
case class BarInstance() extends Bar[Int] {
def get(a: Int): Int = a
}
case class FooTwoInstance[A <: Bar[Int]](`type`: Int) extends FooTwo[A]
case class FooInstance(fooTwo: FooTwo[BarInstance]) extends Foo[BarInstance]
sealed trait FooTwo[A <: Bar[Int]] {
val `type`: Int
}
sealed trait Foo[A <: Bar[Int]] {
val fooTwo: FooTwo[A]
}
implicit val codec: JsonValueCodec[Foo[BarInstance]] =
JsonCodecMaker.make(CodecMakerConfig.withDiscriminatorFieldName(Some("hint")))
def main(args: Array[String]): Unit = {
val x: Foo[BarInstance] = FooInstance(FooTwoInstance(1))
val json = writeToString(x)
println(json)
println(readFromString[Foo[BarInstance]](json))
}
}
Bellow is an output, please check if it satisfies your expectations.
{"hint":"FooInstance","fooTwo":{"hint":"FooTwoInstance","type":1}}
FooInstance(FooTwoInstance(1))
Probably, primitive routines of jsoniter-scala-macros implementation can be easily ported to magnolia.