Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ADT Serialization: Recursive types (possible) / Collection of recursive types (not deriving/compiling) #357

Open
samuelsayag opened this issue Jun 24, 2021 · 5 comments

Comments

@samuelsayag
Copy link
Contributor

Disclaimer:
I am new to zio-json and new to magnolia and couldn't have a convincing answer for my problem in the discord channel so I came here to describe it because it seems to me that there is really some potential problem/enhancement to do.
By all means sorry if it is due to my misunderstanding :)

For the moment it is trivial and very comfortable to serialize this kind of recursive structure:

object KingDom {
  sealed trait Human
  object Human {
    final case class Subject(name: String) extends Human
    final case class King( name: String, reignOver: Human) extends Human
  }
}

With:

object KindDomApp extends App {
  import KingDom.Human
  import KingDom.Human._
  val dom: Human = King("Louis XIV", Subject("Richelieu"))
  println(dom.toJson)
}

It serialize correctly to:

{"King":{"name":"Louis XIV","reignOver":{"Subject":{"name":"Richelieu"}}}}

But we would like to give the King much more subjects with something like (reignOver field is now a collection):

object KingDom {
  sealed trait Human
  object Human {
    final case class Subject(name: String) extends Human
    final case class King(name: String, reignOver: Iterable[Human])
        extends Human

    implicit val codec = DeriveJsonCodec.gen[Human]
  }
}

We get the error(trying Seq/List isn't changing anything):

[E]      magnolia: could not find JsonCodec.Typeclass for type Iterable[zio.aerospike.sandbox.KingDom.Human]
[E]          in parameter 'reignOver' of product type zio.aerospike.sandbox.KingDom.Human.King
[E]          in coproduct type zio.aerospike.sandbox.KingDom.Human
[E]
[E]      L161:     implicit val codec = DeriveJsonCodec.gen[Human]
[E]                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^

As the derivation is made by magnolia I went digging there and there is some example of derivation of Seq type classes so it should be possible.
The thing is all the instances are in the JsonEncoder/Decoder/Codec companion object (seq/iterable...) and I don't know how it is interacting with magnolia derivation for the moment.

I am ready to help and try to solve but I would need some basic direction. Maybe also a more precise diagnostic than what I have done to be sure what is wrong. Thanks for helping.

Cheers!

@fsvehla
Copy link
Contributor

fsvehla commented Jul 7, 2021

If I understand correctly, your problem was solved in the Discord room. If I’m mistaken, please reopen.

@fsvehla fsvehla closed this as completed Jul 7, 2021
@samuelsayag
Copy link
Contributor Author

Hi @fsvehla, the problem is not solved.

I tried the solution of concrete structure such as List as advised by @fommil but the problem persist.
I simply happen to have a weaker case that did not need this exact type of recursion so was eventually was not blocked.

@plokhotnyuk recognized it as a bug and also opened it in jsoniter (plokhotnyuk/jsoniter-scala#757).

He kindly sent me some info about how he solved the problem (in jsoniter) by doing this:

Hi, Samuel! It happened that types not always can be compared by the == operator. I've fixed the issue in jsoniter-scala by using full names for testing of equality of types: plokhotnyuk/jsoniter-scala@3bda152

I thought it would be important to support this kind of case as recursive structure.

If it is considered a minor issue though it can be closed.

@fsvehla fsvehla reopened this Jul 9, 2021
@tbarabas
Copy link

tbarabas commented Jun 8, 2023

I think I reproduced the same issue with the following code, but I am getting a null reference exception from java:

case class A(
              s: String,
              a: Option[Seq[A]]
            )

object A {
  implicit lazy val decoder: JsonDecoder[A] = DeriveJsonDecoder.gen[A]
  implicit lazy val encoder: JsonEncoder[A] = DeriveJsonEncoder.gen[A]
}

object JsonSpec extends ZIOSpecDefault {
  val spec: Spec[Any, Throwable] = suite("SelectionIdHashSpec")(
    test("Find 1-1-1 in ceId test") {
      val json = "{\"s\": \"test\", \"a\":[{\"s\": \"test\"}] }"
      val obj = json.fromJson[A]
      assertTrue(obj.toString == "...")
    }
  )
}

@joroKr21
Copy link

joroKr21 commented Jun 8, 2023

Shouldn't it be lazy val if your type is recursive?

@alexander-klimov
Copy link

So, what does one do with this?
Just got stuck with the same problem at work - can't create derived Decoder for a recursive type.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants