Skip to content

Instantly share code, notes, and snippets.

@xuwei-k
Forked from negator/shapeless-json.scala
Last active August 29, 2015 14:06
Show Gist options
  • Save xuwei-k/f00407af9a279f0c4be5 to your computer and use it in GitHub Desktop.
Save xuwei-k/f00407af9a279f0c4be5 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]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment