Skip to content

Instantly share code, notes, and snippets.

@kolemannix
Created September 25, 2017 16:00
Show Gist options
  • Save kolemannix/b22875b3bd9012e09223ffef7fe6e271 to your computer and use it in GitHub Desktop.
Save kolemannix/b22875b3bd9012e09223ffef7fe6e271 to your computer and use it in GitHub Desktop.
Wrapper-type Play JSON format automatic derivation using shapeless
import play.api.libs.json._
import shapeless.ops.hlist.IsHCons
import shapeless.{ Generic, HList, HNil }
/**
* Typeclass for automatically deriving a [play.api.libs.json.Format] instance for a type `A`.
* Instances will exist if A is a single-member case class containing a Formattable field
*/
trait WrapperJsonFormat[A] {
def jsonFormat: Format[A]
}
object WrapperJsonFormat {
/**
* Summoner method
*/
def apply[A](implicit instance: WrapperJsonFormat[A]): WrapperJsonFormat[A] = instance
/**
* Instance constructor method
*/
def make[A](format: Format[A]): WrapperJsonFormat[A] = new WrapperJsonFormat[A] {
override def jsonFormat: Format[A] = format
}
def make[A](reads: Reads[A], writes: Writes[A]): WrapperJsonFormat[A] = make(Format(reads, writes))
implicit def implyAllWrappers[A, R <: HList, H](
implicit
gen: Generic.Aux[A, R],
isHCons: IsHCons.Aux[R, H, HNil],
formatH: Format[H]
): WrapperJsonFormat[A] = {
make[A](
reads = Reads { jsValue =>
val asH = jsValue.as[H](formatH)
/* Can I avoid this? How do I prove the value (asString :: HNil) <: R? */
val asRepr = (asH :: HNil).asInstanceOf[R]
val a: A = gen.from(asRepr)
JsSuccess(a)
},
writes = Writes { a =>
val asR = gen.to(a)
val x: H = isHCons.head(asR)
formatH.writes(x)
}
)
}
}
// Usage
case class UserId(value: String)
implicit val format: Format[UserId] = WrapperJsonFormat[UserId].jsonFormat
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment