Created
February 12, 2014 13:07
-
-
Save mandubian/8955241 to your computer and use it in GitHub Desktop.
Reads/Writes for a simple AST
This file contains hidden or 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._ | |
sealed trait Parent { | |
val typ: String | |
} | |
case class Foo(blabla: Long, override val typ: String = "foo") extends Parent | |
object Foo { | |
// NOT IMPLICIT because Writes is contravariant so Writes[Parent] would collide with this one | |
val writer = Json.writes[Foo] | |
// CAN BE IMPLICIT (Reads is invariant) | |
implicit val reader = Json.reads[Foo] | |
} | |
case class Bar(blibli: String, override val typ: String = "bar") extends Parent | |
object Bar { | |
// NOT IMPLICIT because Writes is contravariant so Writes[Parent] would collide with this one | |
val writer = Json.writes[Bar] | |
// CAN BE IMPLICIT (Reads is invariant) | |
implicit val reader = Json.reads[Bar] | |
} | |
object Parent { | |
implicit val writer = Writes[Parent] { | |
case foo: Foo => Json.toJson(foo)(Foo.writer) | |
case bar: Bar => Json.toJson(bar)(Bar.writer) | |
} | |
// for reads, if you want to use the discriminator, you need to trick a bit & "cast" to Parent as Reads is invariant | |
implicit val reader = (__.read[JsValue] and (__ \ "typ").read[String]).tupled.flatMap { | |
case (js, "foo") => Reads{ _ => Json.fromJson[Foo](js).map(f => f:Parent) } | |
case (js, "bar") => Reads{ _ => Json.fromJson[Bar](js).map(f => f:Parent) } | |
case _ => Reads[Parent] { _ => JsError("expected either foo or bar") } | |
} | |
} | |
scala> val bar : Parent = Bar("tata") | |
bar: Parent = Bar(tata,bar) | |
scala> val foo : Parent = Foo(5L) | |
foo: Parent = Foo(5,foo) | |
scala> Json.toJson(foo) | |
res0: play.api.libs.json.JsValue = {"blabla":5,"typ":"foo"} | |
scala> Json.toJson(bar) | |
res1: play.api.libs.json.JsValue = {"blibli":"tata","typ":"bar"} | |
scala> Json.fromJson[Parent](Json.parse("""{"blibli":"tata","typ":"bar"}""")) | |
res6: play.api.libs.json.JsResult[Parent] = JsSuccess(Bar(tata,bar),) | |
scala> Json.fromJson[Parent](Json.parse("""{"blibli":"tata","typ":"foo"}""")) | |
res7: play.api.libs.json.JsResult[Parent] = JsError(List((/blabla,List(ValidationError(error.path.missing,WrappedArray()))))) | |
scala> Json.fromJson[Parent](Json.parse("""{"blabla":"tata","typ":"foo"}""")) | |
res8: play.api.libs.json.JsResult[Parent] = JsError(List((/blabla,List(ValidationError(error.expected.jsnumber,WrappedArray()))))) | |
scala> Json.fromJson[Parent](Json.parse("""{"blabla":5,"typ":"foo"}""")) | |
res9: play.api.libs.json.JsResult[Parent] = JsSuccess(Foo(5,foo),) | |
scala> Json.fromJson[Parent](Json.parse("""{"blabla":5,"typ":"foo2"}""")) | |
res0: play.api.libs.json.JsResult[Parent] = JsError(List((,List(ValidationError(expected either foo or bar,WrappedArray()))))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment