Created
October 30, 2015 10:44
-
-
Save jeroenr/47a97849ec1f0c7140ea 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
import play.api.libs.json._ | |
import play.api.libs.functional.syntax._ | |
import HListUtils._ | |
import JsonFormats._ | |
implicit val specReads = ( | |
(__ \ "id").read[String] and | |
(__ \ "specs").read(( | |
(__ \ "specA").read[SpecA] and | |
(__ \ "specB").read[SpecB] | |
).tupled.hlisted) | |
).tupled | |
val json = Json.obj( | |
"id" -> "foo", | |
"specs" -> Json.obj( | |
"specA" -> Json.obj("id" -> "a"), | |
"specB" -> Json.obj("id" -> "b") | |
) | |
) | |
json.validate(specReads) match { | |
case JsSuccess((id,specA :: specB :: HNil), _) => println(s"specs: $specA, $specB") | |
} |
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 org.specs2.mutable.Specification | |
import play.api.libs.functional.Applicative | |
import play.api.libs.json._ | |
import shapeless._ | |
import HList._ | |
import Tuples._ | |
import play.api.libs.functional.syntax._ | |
object HListUtils { | |
private def toHList[H : Reads, T <: HList : Reads](l: List[JsValue])(implicit applicative: Applicative[JsResult]): JsResult[H :: T] = l match { | |
case scala.::(head, tail) => | |
applicative.apply( | |
//JsResult[T => H :: T] | |
applicative.map( | |
implicitly[Reads[H]].reads(head), | |
(h: H) => (t: T) => h :: t | |
), | |
//JsResult[T] | |
implicitly[Reads[T]].reads(JsArray(tail)) | |
) | |
case _ => JsError("can't convert empty list using multi-element HList") | |
} | |
implicit def HNilReads = Reads[HNil]{ js => js match { | |
case JsArray(values) if(values.isEmpty) => JsSuccess(HNil) | |
case JsObject(values) if(values.isEmpty) => JsSuccess(HNil) | |
case _ => JsError("Not empty JsArray or JsObject") | |
} } | |
implicit def hlistReads[H : Reads, T <: HList : Reads](implicit applicative: Applicative[JsResult]) = Reads[H :: T]{ | |
case arr: JsArray => toHList[H, T](arr.value.toList) | |
case obj: JsObject => toHList[H, T](obj.values.toList) | |
case js => toHList[H, T](List(js)) | |
//JsError("Single JsValue can't be mapped to multi-element HList") | |
} | |
implicit def HNilWrites = Writes[HNil]{ hl => JsArray() } | |
/*implicit def hlistHNilWrites[H : Writes] = Writes[H :: HNil]{ hl => | |
val head :: HNil = hl | |
JsArray(Seq(implicitly[Writes[H]].writes(head))) | |
}*/ | |
implicit def hlistWrites[H : Writes, T <: HList : Writes] = Writes[H :: T]{ case hl => | |
implicitly[Writes[H]].writes(hl.head) +: implicitly[Writes[T]].writes(hl.tail).as[JsArray] | |
} | |
implicit class TupleReadsOps[ P <: Product ](r: Reads[P]) { | |
def hlisted(implicit hlister : HLister[P]) = r map { _.hlisted } | |
} | |
implicit class TupleWritesOps[ P <: Product ](w: Writes[P]) { | |
def contramap[A, B](wa:Writes[A], f: B => A): Writes[B] = Writes[B]( b => wa.writes(f(b)) ) | |
def hlisted[T <: HList](implicit hlister : HLister[P], tupler: TuplerAux[T, P]) = contramap(w, (hl: T) => hl.tupled) | |
} | |
def JsArrayIso[L <: HList](implicit r: Reads[L], w: Writes[L]) = new Iso[JsArray, L] { | |
def to(t: JsArray): L = r.reads(t).get | |
def from(l: L): JsArray = w.writes(l).asInstanceOf[JsArray] | |
} | |
} |
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
trait Spec | |
case class SpecA(id: String) extends Spec | |
case class SpecB(id: String) extends Spec | |
object JsonFormats { | |
implicit val specAFormat = Json.format[SpecA] | |
implicit val specBFormat = Json.format[SpecB] | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment