在Play for Scala中将异构列表转换为Json和从Json转换
我试图获取一个共享特征的项目列表,然后将它们转交给Json.我在这里介绍的例子是一辆带有发动机和汽车的火车.创建火车分为三类:发动机,乘用车和货车. (我认为一个简单的基于现实的例子最容易理解,它也比我试图解决的问题复杂得多.)
列车的部分定义如下: package models sealed trait Vehicle { val kind: String val maxSpeed: Int = 0 def load: Int } case class Engine(override val maxSpeed: Int,val kind: String,val power: Float,val range: Int) extends Vehicle { override val load: Int = 0 } case class FreightCar(override val maxSpeed: Int,val load: Int) extends Vehicle {} case class PassengerCar(override val maxSpeed: Int,val passengerCount: Int) extends Vehicle { override def load: Int = passengerCount * 80 } (文件Vehicle.scala) 火车的定义是: package models import scala.collection.mutable import play.api.Logger import play.api.libs.json._ case class Train(val name: String,val cars: List[Vehicle]) { def totalLoad: Int = cars.map(_.load).sum def maxSpeed: Int = cars.map(_.maxSpeed).min } object Train { def save(train: Train) { Logger.info("Train saved ~ Name: " + train.name) } } (档案Train.scala) 如您所见,通过将“汽车”添加到列车中存储的列表来创建火车. 将我的火车换成Json或试图从Json读取时,我的问题就出现了.这是我目前执行此操作的代码: package controllers import play.api.mvc._ import play.api.libs.json._ import play.api.libs.json.Reads._ import play.api.data.validation.ValidationError import play.api.libs.functional.syntax._ import models.Vehicle import models.Engine import models.FreightCar import models.PassengerCar import models.Train class Trains extends Controller { implicit val JsPathWrites = Writes[JsPath](p => JsString(p.toString)) implicit val ValidationErrorWrites = Writes[ValidationError](e => JsString(e.message)) implicit val jsonValidateErrorWrites = ( (JsPath "path").write[JsPath] and (JsPath "errors").write[Seq[ValidationError]] tupled ) implicit object engineLoadWrites extends Writes[Engine] { def writes(e: Engine) = Json.obj( "maxSpeed" -> Json.toJson(e.maxSpeed),"kind" -> Json.toJson(e.kind),"power" -> Json.toJson(e.power),"range" -> Json.toJson(e.range) ) } implicit object freightCarLoadWrites extends Writes[FreightCar] { def writes(fc: FreightCar) = Json.obj( "maxSpeed" -> Json.toJson(fc.maxSpeed),"kind" -> Json.toJson(fc.kind),"load" -> Json.toJson(fc.load) ) } implicit object passengerCarLoadWrites extends Writes[PassengerCar] { def writes(pc: PassengerCar) = Json.obj( "maxSpeed" -> Json.toJson(pc.maxSpeed),"kind" -> Json.toJson(pc.kind),"passengerCount" -> Json.toJson(pc.passengerCount) ) } implicit object trainWrites extends Writes[Train] { def writes(t: Train) = Json.obj( "name" -> Json.toJson(t.name),"cars" -> Json.toJson(t.cars) // Definitely not correct! ) } /* --- Writes above,Reads below --- */ implicit val engineReads: Reads[Engine] = ( (JsPath "maxSpeed").read[Int] and (JsPath "kind").read[String] and (JsPath "power").read[Float] and (JsPath "range").read[Int] )(Engine.apply _) implicit val freightCarReads: Reads[FreightCar] = ( (JsPath "maxSpeed").read[Int] and (JsPath "kind").read[String] and (JsPath "load").read[Int] )(FreightCar.apply _) implicit val passengerCarReads: Reads[PassengerCar] = ( (JsPath "maxSpeed").read[Int] and (JsPath "kind").read[String] and (JsPath "passengerCount").read[Int] )(PassengerCar.apply _) implicit val joistReads: Reads[Train] = ( (JsPath "name").read[String](minLength[String](2)) and (JsPath "cars").read[List[Cars]] // Definitely not correct! )(Train.apply _) /** * Validates a JSON representation of a Train. */ def save = Action(parse.json) { implicit request => val json = request.body json.validate[Train].fold( valid = { train => Train.save(train) Ok("Saved") },invalid = { errors => BadRequest(Json.toJson(errors)) } ) } } (File Trains.scala) 所有用于为FreightCars列表创建和使用Json的代码,Engines都可以,但我无法创建Json来同时处理所有三种类型,例如: implicit object trainWrites extends Writes[Train] { def writes(t: Train) = Json.obj( "name" -> Json.toJson(t.name),"cars" -> Json.toJson(t.cars) ) } 列表的Json.toJson根本不起作用;也没有阅读对应物.当我用类Engines或我的任何单个具体类替换上面代码中的t.cars时,一切正常. 我怎样才能优雅地解决这个问题,让我的Json读者和作家工作?或者,如果Scala Play的Json编码器解码器不适合这样的任务,那么是否有更合适的Json库? 解决方法
运行您的Writes代码会返回以下错误(这给出了修复内容的线索):
Error:(78,27) No Json deserializer found for type Seq[A$A90.this.Vehicle]. Try to implement an implicit Writes or Format for this type. "cars" -> Json.toJson(t.cars) // Definitely not correct! ^ 没有为Vehicle找到解串器,因此您需要为Vehicle添加读/写(或格式).这将只委托给该类型的实际格式. 对于写入,实现非常简单,可以根据类型进行模式匹配.对于读取,我正在寻找json中的区别属性,以指示要委派的读取内容.
//Question code above,then ... val engineFormat = Json.format[Engine] val freightCarFormat = Json.format[FreightCar] val passengerCarFormat = Json.format[PassengerCar] implicit val vehicleFormat = new Format[Vehicle]{ override def writes(o: Vehicle): JsValue = { o match { case e : Engine => engineFormat.writes(e) case fc : FreightCar => freightCarFormat.writes(fc) case pc : PassengerCar => passengerCarFormat.writes(pc) } } override def reads(json: JsValue): JsResult[Vehicle] = { (json "power").asOpt[Int].map{ _ => engineFormat.reads(json) }.orElse{ (json "passengerCount").asOpt[Int].map{ _ => passengerCarFormat.reads(json) } }.getOrElse{ //fallback to FreightCar freightCarFormat.reads(json) } } } implicit val trainFormat = Json.format[Train] val myTrain = Train( "test",List( Engine(100,"e-1",1.0.toFloat,100),FreightCar(100,"f-1",20),PassengerCar(100,"pc",10) ) ) val myTrainJson = trainFormat.writes(myTrain) /** => myTrainJson: play.api.libs.json.JsObject = {"name":"test","cars":[{"maxSpeed":100,"kind":"e-1","power":1.0,"range":100},{"maxSpeed":100,"kind":"f-1","load":20},"kind":"pc","passengerCount":10}]} */ val myTrainTwo = myTrainJson.as[Train] /* => myTrainTwo: Train = Train(test,List(Engine(100,e-1,1.0,f-1,pc,10))) */ (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |