条件JSON读取scala

时间:2017-09-10 03:10:20

标签: json scala playframework

我有两个继承自普通父级的类。我希望在父级中有一个通用的JSON读取器,它将根据提供的JSON返回一个合适的子级。使用下面的示例代码段更容易解释这一点;

import play.api.libs.json.{JsPath, JsonValidationError, Reads}

sealed abstract class Animal(sound: String)

case class Goat(hooves: String) extends Animal("meh")

case class Cat(needsMilk: Boolean) extends Animal("meow")

val json ="""{"type": "goat", "hooves": "All good for climbing trees"}"""

object Animal {
  val isSupportedAnimal: Reads[String] =
    Reads.StringReads.filter(JsonValidationError("Unsupported animal"))(str => {
      List("goat", "cat").contains(str)
    })

  val animalReads: Reads[Animal] = ((JsPath \ "type").read[String](isSupportedAnimal) and
    //if animal is cat, use the cat specific reads and return a cat object
    //if animal is goat, use goat specific reads and return a goat
    )()
}

鉴于代码段中的json,我希望拥有Goat对象,因为指定的类型为goat

我是scala的新手,所以我可能会以错误的方式解决问题。欢迎提出建议。

2 个答案:

答案 0 :(得分:4)

使用Map

sealed abstract class Animal(val sound: String) // You probably want a val here, btw
final case class Goat(hooves: String) extends Animal("meh")
final case class Cat(needsMilk: Boolean) extends Animal("meow")

object Animal {
  val readers: Map[String, Reads[_ <: Animal]] = Map(
    "goat" -> implicitly[Reads[Goat]],
    "cat" -> implicitly[Reads[Cat]],
    // Sidenote: Trailing commas   ^ make future modification easy
  )
  // Bonus: Set[String] <: String => Boolean, so you get isSupportedAnimal for free
  // val isSupportedAnimal: String => Boolean = readers.keys

  implicit val animalReads: Reads[Animal] = new Reads[Animal] {
    def reads(s: JsValue): JsResult[Animal] = {
      val tpe = (s \ "type").as[String]
      val read = readers.get(tpe)
      read.map(_.reads(s)).getOrElse(JsError(s"Unsupported animal: $tpe"))
    }
  }
}

如果您不想使用此样板文件,可以查看this library(使用无形)。

答案 1 :(得分:1)

您可以尝试制作这样的自定义阅读器:

implicit val animalReads = new Reads[Animal] {
    def reads(js: JsValue): Animal = {
      (js \ "type").as[String] match {
        case "cat" => Cat( (js \ "needsMilk").as[Boolean] )
        case "goat" => Goat( (js \ "hooves").as[String] )
        case _ => throw new JsonValidationError("Unsupported animal")
      }
    }
  }