Skip to content

Instantly share code, notes, and snippets.

@negator
Last active January 15, 2019 13:34
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save negator/5139ddb5f6d91cbe7b0c to your computer and use it in GitHub Desktop.
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]
@ergomesh
Copy link

ergomesh commented Nov 5, 2014

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

@evelant
Copy link

evelant commented Nov 23, 2014

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?

@angeloh
Copy link

angeloh commented Apr 6, 2015

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

@victrcodes
Copy link

This no longer works as of play 2.4.3 and shapeless 2.2.5

@feliperazeek
Copy link

@negator ❤️

@aboyett
Copy link

aboyett commented May 13, 2016

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment