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

Traits and JsonCodecMaker #247

Open
dalegaspi opened this issue Mar 12, 2019 · 3 comments
Open

Traits and JsonCodecMaker #247

dalegaspi opened this issue Mar 12, 2019 · 3 comments
Labels

Comments

@dalegaspi
Copy link

dalegaspi commented Mar 12, 2019

This is perhaps a general misunderstanding on how the JsonCodecMaker works, but i'm struggling with using the macro when one field is a trait rather than a case class. consider this example:

trait OneTypable {
  def name: String
}

case class OneType(name: String) extends OneTypable

case class ClassUseOneType(id: String, oneType: OneTypable)

when i use the macro:

implicit JsonValueCodec[ClassUseOneType] = JsonCodecMaker.make[ClassUseOneType](CodecMakerConfig(allowRecursiveTypes = true))

i get a compile time error:

No implicit 'com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec[_]' defined for 'OneTypable'.

i'm not entirely sure how to get around this or if i've hit a limitation to what the macro can do.

thank you.

@plokhotnyuk
Copy link
Owner

plokhotnyuk commented Mar 12, 2019

jsoniter-scala derives codecs only for sealed traits.

Generally it is done for security reasons to avoid generation of codecs for unwanted implementations (which can presents in the compilation classpath) of generic traits like Serializable, Cloneable, etc.

Also, during code generation the make macro checks that all non-abstract sub-classes of the sealed trait will have unique discriminator value for type key (as in example bellow).

Here is how it works for sealed trait:

sealed trait OneTypable {
  def name: String
}

case class OneType(name: String) extends OneTypable

case class ClassUseOneType(id: String, oneType: OneTypable)


implicit val classUseOneTypeCodec: JsonValueCodec[ClassUseOneType] = 
  JsonCodecMaker.make[ClassUseOneType](CodecMakerConfig())

val x = readFromArray("""{"id":"XXX","oneType":{"type":"OneType","name":"YYY"}}""".getBytes("UTF-8"))
val json = writeToArray(ClassUseOneType(id = "XXX", oneType = OneType(name = "YYY")))

println(x)
println(new String(json, "UTF-8"))

Another option is to use wrapping by discriminator JSON object instead of the type property:

sealed trait OneTypable {
  def name: String
}

case class OneType(name: String) extends OneTypable

case class ClassUseOneType(id: String, oneType: OneTypable)


implicit val classUseOneTypeCodec: JsonValueCodec[ClassUseOneType] = 
  JsonCodecMaker.make[ClassUseOneType](CodecMakerConfig(discriminatorFieldName = None))

val user = readFromArray("""{"id":"XXX","oneType":{"OneType":{"name":"YYY"}}}""".getBytes("UTF-8"))
val json = writeToArray(ClassUseOneType(id = "XXX", oneType = OneType(name = "YYY")))

println(user)
println(new String(json, "UTF-8"))

@plokhotnyuk
Copy link
Owner

plokhotnyuk commented Mar 12, 2019

BTW, if models for serialization to XML and JSON differs too much then you can consider an ability to generate a separate model tree for JSON representation from some JsonSchema.

Also, a mapping between model trees can be defined by the Chimney library to avoid most of boilerplate.

Yet another option would be writing of custom codecs or a custom macro for their generation... Here is example of custom codecs for a simple model: https://github.com/plokhotnyuk/jsoniter-scala/blob/master/jsoniter-scala-core/src/test/scala/com/github/plokhotnyuk/jsoniter_scala/core/UserAPI.scala

@dalegaspi
Copy link
Author

@plokhotnyuk ..once again, i appreciate the prompt and thorough response. as you may have probably guessed...just like in my other question, i am using scalaxb to generate the case classes...and it seems that this can be addressed by making the base trait generated as sealed (i opened a ticket or hey maybe i'll attempt to make a PR for that ticket...god help us)

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

No branches or pull requests

2 participants