【问题标题】:Scala Play framework 2.1 derived classesScala Play framework 2.1 派生类
【发布时间】:2013-07-15 02:15:01
【问题描述】:

我想为包含基类和派生类的 List 生成 JSON。下面的代码只为 Animal 类生成 JSON(我没有得到 Dog 类型成员的品种字段)。一些帮助将不胜感激。

import play.api.libs.json._

class Animal (val name:String) {
}

object Animal {
  implicit object animalWrite extends Writes[Animal] {
    def writes(ts: Animal) = JsObject(Seq("name" -> JsString(ts.name)))
  }
}

case class Dog (override val name:String, val breed: String) 
    extends Animal(name)  {
}

object Dog {
    implicit val format = Json.format[Dog]
}

case class Cat (override val name:String, val hairLength: Int) 
    extends Animal(name)  {
}

object Cat {
    implicit val format = Json.format[Cat]
}

object helloWorld extends App {
//  The list below outputs:     [{"name":"Ruff","breed":"labrador"}]
//  val l = List[Dog](Dog("Ruff", "labrador"))

//  The list below outputs:     [{"name":"Ruff"},{"name":"Fluffy"}]
//  I expect to see: [{"name":"Ruff","breed":"labrador"},{"name":"Fluffy","hairLength":3}]
    val l = List[Animal](Dog("Ruff", "labrador"), Cat("Fluffy", 3))
    println(Json.toJson(l))
}

Scala 和 Play 新手在这里,请原谅不当使用的术语。

【问题讨论】:

    标签: scala polymorphism playframework-2.1


    【解决方案1】:

    json API 广泛使用隐式参数,这是 Scala 的一项功能,您可以在其中提供“隐式”参数列表,如果您不指定这些参数,编译器将尝试在当前范围内查找标记的对象作为隐式并匹配该签名。

    因此,例如,如果您会这样写:

    implicit val s = "my implicit string"
    
    def magicPrint(implicit message: String) { println(message) }
    
    // and then call it
    magicPrint
    

    编译器会为参数消息选择 s,因为它在范围内并且具有正确的类型 (String),因此在隐式解析之后,最后一行代码实际上看起来更像这样

    magicPrint(s)
    

    编译器在编译时使用隐式参数选择格式/写入器。如果您查看方法的签名toJson[A](item: A)(implicit writes: Writes[A]),它需要一个隐含的Writes[A],在您的情况下是Writes[List[Animal]],因为List[Animal] 是您的列表l 的类型。 Play 包含一个默认的编写器,该编写器负责处理集合 (DefaultWrites.traversableWrites),该集合又采用隐式 Writes[A] - 在您的情况下为 Writes[Animal],因此编译器将选择并传递您的 animalWrites。

    您的列表包含不同类型的动物是在运行时发生的事情,编译器无法从您的 Json.toJson(l) 提供的类型信息中知道这一点

    所以,正如您所见,您无法以您想的方式实现您想要的,但您可以通过让动物作家了解亚型,以几乎相同的方式做到这一点,例如:

    implicit object animalWrite extends Writes[Animal] {
      def writes(ts: Animal) = ts match {
        // this will get an implicit Writes[Dog] since d is a Dog
        case d: Dog => Json.toJson(d) 
        // this will get an implicit Writes[Cat] since c is a Cat
        case c: Cat => Json.toJson(c) 
        case x => throw new RuntimeException(s"Unknown animal $x")
      }
    }
    

    希望这有帮助!

    【讨论】:

    • 谢谢约翰。这很有帮助,我认为这个问题与隐式有关,但我不明白如何。我为解决这种情况所做的是在 Dog 和 Cat 中实现一个序列化方法,该方法调用 Json.toJson(this) 并使用 l.map(_.serialize).mkString("[", ",", "]") 调用它。你的更好,因为你不必像我一样手动将 Json 连接在一起。谢谢。
    • 在动物女巫中添加一个函数 toJson 不是更好,可以这样实现 ``` case class Dog (override val name:String, val品种: String) extends Animal(name) { def toJson = Json.toJson(this) } ```
    • 通常会避免这样做,因为它会将您的域模型与一种序列化形式的实现细节混合并紧密耦合,而播放 json 的类型类通常会很好地解耦。
    猜你喜欢
    • 2013-01-06
    • 2012-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多