Last active
March 7, 2018 04:42
-
-
Save steinybot/85b42fc375d3767ab5ed0dbe41bcaa23 to your computer and use it in GitHub Desktop.
Play Json Reads and Writes for Coproducts
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
package com.geneious.nucleus.service.util.play.json | |
import play.api.libs.json._ | |
import shapeless._ | |
trait CoproductFormats { | |
implicit val cNilReads: Reads[CNil] = Reads(_ => JsError()) | |
implicit val cNilWrites: Writes[CNil] = Writes(_ => JsNull) | |
implicit val cNilOWrites: OWrites[CNil] = OWrites(_ => JsObject.empty) | |
final class CoproductReads[HT] { | |
def apply[H: Reads, T <: Coproduct : Reads]()(implicit evidence: (H :+: T) =:= HT): Reads[HT] = { | |
implicitCoproductReads[H, T].map(evidence) | |
} | |
} | |
def coproductReads[HT]: CoproductReads[HT] = new CoproductReads[HT] | |
implicit def implicitCoproductReads[H: Reads, T <: Coproduct : Reads]: Reads[H :+: T] = { | |
val hReads = implicitly[Reads[H]].map[H :+: T](Inl[H, T]) | |
val tReads = implicitly[Reads[T]].map[H :+: T](Inr[H, T]) | |
JsonReads.firstReads(hReads, tReads) | |
} | |
final class CoproductWrites[HT] { | |
def apply[H: Writes, T <: Coproduct : Writes]()(implicit evidence: HT =:= (H :+: T)): Writes[HT] = { | |
Writes[HT] { | |
ht => implicitCoproductWrites[H, T].writes(evidence(ht)) | |
} | |
} | |
} | |
def coproductWrites[HT]: CoproductWrites[HT] = new CoproductWrites[HT] | |
def implicitCoproductWrites[H: Writes, T <: Coproduct : Writes]: Writes[H :+: T] = { | |
Writes[H :+: T] { | |
case Inl(head) => implicitly[Writes[H]].writes(head) | |
case Inr(tail) => implicitly[Writes[T]].writes(tail) | |
} | |
} | |
final class CoproductOWrites[HT] { | |
def apply[H: OWrites, T <: Coproduct : OWrites]()(implicit evidence: HT =:= (H :+: T)): OWrites[HT] = { | |
OWrites[HT] { ht => | |
implicitCoproductOWrites[H, T].writes(evidence(ht)) | |
} | |
} | |
} | |
def coproductOWrites[HT]: CoproductOWrites[HT] = new CoproductOWrites[HT] | |
def implicitCoproductOWrites[H: OWrites, T <: Coproduct : OWrites]: OWrites[H :+: T] = { | |
OWrites[H :+: T] { | |
case Inl(head) => implicitly[OWrites[H]].writes(head) | |
case Inr(tail) => implicitly[OWrites[T]].writes(tail) | |
} | |
} | |
} | |
object CoproductFormats extends CoproductFormats |
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
import play.api.libs.json._ | |
import scala.annotation.tailrec | |
object JsonReads { | |
def firstReads[A](read: Reads[_ <: A], reads: Reads[_ <: A]*): Reads[A] = { | |
@tailrec | |
def loop(json: JsValue, remaining: Seq[Reads[_ <: A]], error: JsError): JsResult[A] = remaining match { | |
case head +: tail => head.reads(json) match { | |
case headError: JsError => loop(json, tail, JsError.merge(error, headError)) | |
case success: JsSuccess[A] => success | |
} | |
case Seq() => error | |
} | |
Reads(loop(_, read +: reads, JsError())) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment