bizzabo / play-json-extensions

+22 field case class formatter and more for play-json
http://cvogt.org/play-json-extensions/api/
Other
196 stars 44 forks source link

Jsonx formatCaseClass support for generic case class #58

Open ashishkujoy opened 5 years ago

ashishkujoy commented 5 years ago

As a user, I want to ability to get JSON format for a case class using generic type.

Given

trait SomeTrait
case class ClassA(a:String, b:String) extends SomeTrait
case class ClassB(a:String) extends SomeTrait
case class CompositeClass[A <: SomeTrait](name:String,generics:A)
object ClassA {
   implicit val classAJsonFormat = Jsonx.formatCaseClass[ClassA]
}
object ClassB {
   implicit val classBJsonFormat = Jsonx.formatCaseClass[ClassB]
}
object CompositeClass {
   implicit def compositeClassJsonFormat[A](implicit aft:OFormat[A]) = Jsonx.formatCaseClass[CompositeClass[A]]
}

Expected behaviour No compilation and runtime error

Current behaviour

Error:(17, 107) could not find implicit value for parameter helper: play.api.libs.json.Reads[A]
TRIGGERED BY: could not find implicit value for parameter helper: ai.x.play.json.OptionValidationDispatcher[A]
TO SOLVE THIS
1. Make sure there is a Reads[A] or Format[A] in the implicit scope
2. In case of Reads[Option[...]] you need to either
   import ai.x.play.json.implicits.optionWithNull // suggested
   or
   import ai.x.play.json.implicits.optionNoError // buggy play-json 2.3 behavior
3. In case of Reads[... .type]
   import ai.x.play.json.SingletonEncoder.simpleName
   import ai.x.play.json.implicits.formatSingleton
  implicit def reads[A <: SomeTrait](implicit format: Format[A]): OFormat[CompositeClass[A]] = Jsonx.formatCaseClass[CompositeClass[A]]
Arikuti commented 5 years ago

I am facing the same issue

d-william commented 3 years ago

I still have this issue

ashishkujoy commented 3 years ago

@d-william Since other people are also facing this issue I am reopening this, but it does not looks like this project is actively maintained, even last time I have close this as there was no response from the maintainers.

caente commented 3 years ago

@ashishkujoy you are right in that is not actively maintained, its original maintainer left long ago, and I have been "keeping the lights on". I will try to take a look asap to this issue, and maybe do some spring cleaning on others that may be still pending

caente commented 3 years ago

@cvogt perhaps I can summon you to help out here?

caente commented 3 years ago

well this works:

sealed trait SomeTrait
case class ClassA(a: String, b: String ) extends SomeTrait
case class ClassB(a: String ) extends SomeTrait
case class CompositeClass[A](name: String, generics: A )
object ClassA {
  implicit val classAJsonFormat = Json.format[ClassA]
}
object ClassB {
  implicit val classBJsonFormat = Json.format[ClassB]
}
object SomeTrait {
  implicit val someTraitFormat = Jsonx.formatSealed[SomeTrait]
}
object CompositeClass {
  implicit def compositeClassJsonFormat[A](implicit aft: OFormat[A] ) = Json.format[CompositeClass[A]]
}

Notice a few things: 1 - I made SomeTrait sealed, hopefully this won't break your use case 2 - I removed the constraint on CompositeClass[A] 3 - I added a formatter for SomeTrait 4 - I only used this lib for formatting of the sealed trait, the rest can just use the regular play-json formatter

caente commented 3 years ago

actually I realized you didn't care about serializing SomeTrait, therefore you don't needs this lib at all:

trait SomeTrait
case class ClassA(a: String, b: String ) extends SomeTrait
case class ClassB(a: String ) extends SomeTrait
case class CompositeClass[A <: SomeTrait](name: String, generics: A )
object ClassA {
  implicit val classAJsonFormat = Json.format[ClassA]
}
object ClassB {
  implicit val classBJsonFormat = Json.format[ClassB]
}

object CompositeClass {
  implicit def compositeClassJsonFormat[A <: SomeTrait](implicit aft: OFormat[A] ) = Json.format[CompositeClass[A]]
}

test( "print stuff" ) {
    println( Json.toJson( CompositeClass( "yeah", ClassA( "a", "b" ) ) ) ) 
  }

prints:

{"name":"yeah","generics":{"a":"a","b":"b"}}
caente commented 3 years ago

personally I mostly have three use cases for this lib: 1 - case classes with default values 2 - sealed traits 3 - when I want to automate how case objects are serialized, e.g. camel case, snake case, etc

dhoepelman commented 3 years ago

1 and 3 are natively possible in play-json versions (Json.using[Json.WithDefaultValues].format and JsonConfiguration(SnakeCase) respectively)

The main advantage for me is that this lib turnings thrown exceptions into deserialization errors

ashishkujoy commented 3 years ago

The play json handles generics, my main reason for using this library back in 2018 was that this library had support for serialising case class of more than 22 fields which was not present in play json.

caente commented 3 years ago

back in 2018 was that this library had support for serialising case class of more than 22 fields which was not present in play json.

that is in fact the reason why @cvogt started this lib in the first place :)

caente commented 3 years ago

The main advantage for me is that this lib turnings thrown exceptions into deserialization errors

that is a fair point

in any case, I am as familiar with the codebase of this lib as any of you, so if you are interested in making PRs, am in interested in merging them :)

alejandrod-f commented 1 year ago

It will be great to have this.