Skip to content

Instantly share code, notes, and snippets.

@domdorn
Created September 18, 2020 12:54
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save domdorn/46299b2013b8837566cdf8aa23541462 to your computer and use it in GitHub Desktop.
Save domdorn/46299b2013b8837566cdf8aa23541462 to your computer and use it in GitHub Desktop.
Play Json Inheritance
import play.api.libs.json._
sealed trait Animal {
val `type`: String
}
case class Cat(name: String) extends Animal {
override val `type`: String = "cat"
}
case class Dog(name: String) extends Animal {
override val `type`: String = "dog"
}
trait AnimalJsonSupport
extends de.heikoseeberger.akkahttpplayjson.PlayJsonSupport {
implicit val catReads: Reads[Cat] =
(JsPath \ "name").read[String].map(Cat(_))
implicit val dogReads: Reads[Dog] =
(JsPath \ "name").read[String].map(Dog(_))
implicit val catWrites: Writes[Cat] = e =>
JsObject(Seq("type" -> JsString("cat"), "name" -> JsString(e.name)))
implicit val dogWrites: Writes[Dog] = e =>
JsObject(Seq("type" -> JsString("dog"), "name" -> JsString(e.name)))
implicit val animalReads: Reads[Animal] = Reads[Animal](jsValue => {
(jsValue \ "type").toEither
.map(
v =>
v.as[String] match {
case "cat" => catReads.map(_.asInstanceOf[Animal]).reads(jsValue)
case "dog" => dogReads.map(_.asInstanceOf[Animal]).reads(jsValue)
case t =>
JsError((JsPath \ "type"), "unkown animal type $t")
.asInstanceOf[JsResult[Animal]]
}
)
.fold(v => JsError(JsPath, v).asInstanceOf[JsResult[Animal]], v => v)
})
implicit val animalWrites: Writes[Animal] = Json.writes[Animal]
}
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
import play.api.libs.json.Json
class AnimalJsonSupportSpec
extends AnyWordSpec
with Matchers
with AnimalJsonSupport
{
val catJsonString =
"""
|{
|"type":"cat",
|"name":"test"
|}
|""".stripMargin.replace("\n", "")
val dummyRequestResponse = Cat("test")
"The AnimalJsonSupport" when {
"handling the Cat-Type" should {
"correctly serialize the response" in {
Json.toJson(dummyRequestResponse).toString() should ===(catJsonString)
}
"correctly deserialize the cat response" in {
val parseResult = Json.fromJson[Animal](Json.parse(catJsonString))
parseResult.get should ===(dummyRequestResponse)
}
}
"handling the Dog-Type" should {
"correctly deserialize the dog response" in {
val json =
"""
|{
|"type":"dog",
|"name":"test2"
|}
|""".stripMargin.replace("\n", "")
val parseResult = Json.fromJson[Animal](Json.parse(json))
parseResult.get should ===(Dog("test2"))
}
}
"handling other types" should {
"fail trying to deserialize a unknown type" in {
val json = """
|{
|"type":"unknown",
|"name":"test"
|}
|""".stripMargin.replace("\n", "")
val result = Json.fromJson[Animal](Json.parse(json))
result.isError shouldBe(true)
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment