我想为包含基本类和派生类的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新手在这里,请原谅术语使用不当。
最佳答案
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)
编译器在编译时使用隐式参数选择Format/Writer。如果查看方法
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")
}
}
希望这对您有所帮助!