-
-
Save negator/5139ddb5f6d91cbe7b0c to your computer and use it in GitHub Desktop.
/** | |
The Play (2.3) json combinator library is arguably the best in the scala world. However it doesnt | |
work with case classes with greater than 22 fields. | |
The following gist leverages the shapeless 'Automatic Typeclass Derivation' facility to work around this | |
limitation. Simply stick it in a common location in your code base, and use like so: | |
Note: ** Requires Play 2.3 and shapeless 2.0.0 | |
import SWrites._ | |
import SReads._ | |
case class Foo(value: String) | |
case class Bar(value1: Int, foo: Foo) //Didnt want to type out 23 fields, but you get the idea | |
implicit val writes: Writes[Foo] = SWrites.auto.derive[Foo] | |
implicit val reads: Reads[Foo] = SReads.auto.derive[Foo] | |
implicit val writes: Writes[Bar] = SWrites.auto.derive[Bar] | |
implicit val reads: Reads[Bar] = SReads.auto.derive[Bar] | |
Additionally, you may get boilerplate free Format typeclasses: | |
import SFormats.auto._ | |
case class Foo(value: String) | |
case class Bar(value1: Int, foo: Foo) | |
def someFunc(value: T)(implicit val format: Format[T]) = ... | |
**/ | |
import play.api.libs._ | |
import json._ | |
import shapeless.{ `::` => :#:, _ } | |
import poly._ | |
implicit val writesInstance: LabelledProductTypeClass[Writes] = new LabelledProductTypeClass[Writes] { | |
def emptyProduct: Writes[HNil] = Writes(_ => Json.obj()) | |
def product[F, T <: HList](name: String, FHead: Writes[F], FTail: Writes[T]) = Writes[F :#: T] { | |
case head :#: tail => | |
val h = FHead.writes(head) | |
val t = FTail.writes(tail) | |
(h, t) match { | |
case (JsNull, t: JsObject) => t | |
case (h: JsValue, t: JsObject) => Json.obj(name -> h) ++ t | |
case _ => Json.obj() | |
} | |
} | |
def project[F, G](instance: => Writes[G], to: F => G, from: G => F) = Writes[F](f => instance.writes(to(f))) | |
} | |
object SWrites extends LabelledProductTypeClassCompanion[Writes] | |
implicit val readsInstance: LabelledProductTypeClass[Reads] = new LabelledProductTypeClass[Reads] { | |
def emptyProduct: Reads[HNil] = Reads(_ => JsSuccess(HNil)) | |
def product[F, T <: HList](name: String, FHead: Reads[F], FTail: Reads[T]) = Reads[F :#: T] { | |
case obj @ JsObject(fields) => | |
for { | |
head <- FHead.reads(obj \ name) | |
tail <- FTail.reads(obj - name) | |
} yield head :: tail | |
case _ => JsError("Json object required") | |
} | |
def project[F, G](instance: => Reads[G], to: F => G, from: G => F) = Reads[F](instance.map(from).reads) | |
} | |
object SReads extends LabelledProductTypeClassCompanion[Reads] | |
implicit val formatInstance: LabelledProductTypeClass[Format] = new LabelledProductTypeClass[Format] { | |
def emptyProduct: Format[HNil] = Format( | |
readsInstance.emptyProduct, | |
writesInstance.emptyProduct | |
) | |
def product[F, T <: HList](name: String, FHead: Format[F], FTail: Format[T]) = Format[F :#: T] ( | |
readsInstance.product[F, T](name, FHead, FTail), | |
writesInstance.product[F, T](name, FHead, FTail) | |
) | |
def project[F, G](instance: => Format[G], to: F => G, from: G => F) = Format[F]( | |
readsInstance.project(instance, to, from), | |
writesInstance.project(instance, to, from) | |
) | |
} | |
object SFormats extends LabelledProductTypeClassCompanion[Format] |
Like @ergomesh I would also like to know the status of this with regards to the play framework. Without being able to model even moderately sized database documents Play seems useless for anything but trivial applications. Is this the "correct" solution going forward for larger projects? Will it be integrated into the play framework?
I got compile errors like this:
expected start of definition
[error] implicit val writesInstance: LabelledProductTypeClass[Writes] = new LabelledProductTypeClass[Writes]
^
Should I use this one?
https://gist.github.com/negator/fbbcd1b2ce9a85b762b7
This no longer works as of play 2.4.3 and shapeless 2.2.5
@negator ❤️
@negator Would you mind indicating what license you're releasing this code under? Both the Play Framework and Shapeless are Apache 2 licensed, but I don't want to make any assumptions about the restrictions placed on this code before using it.
by common location can you give an example of where and how to include that in a default play2 framework? also, is there a reason why this isn't a commit to the next release of play2 the 22 field limitation seems like a major issue for larger projects