Last active
January 15, 2019 13:34
-
-
Save negator/5139ddb5f6d91cbe7b0c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
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] |
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.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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