没有play.api.libs.json.Format的实例可用于隐式范围内的models.AccountStatus。
这是从github页面获取的代码,只更改了类名和变量名。
package models
import slick.jdbc.H2Profile._
import play.api.libs.json._
case class Account(id: Long, name: String, category: Int, status:AccountStatus)
object Account {
implicit val accountFormat = Json.format[Account]
}
sealed abstract class AccountStatus(val as:Int)
object AccountStatus{
final case object Draft extends AccountStatus(0)
final case object Active extends AccountStatus(1)
final case object Blocked extends AccountStatus(2)
final case object Defaulter extends AccountStatus(3)
implicit val columnType: BaseColumnType[AccountStatus] = MappedColumnType.base[AccountStatus,Int](AccountStatus.toInt, AccountStatus.fromInt)
private def toInt(as:AccountStatus):Int = as match {
case Draft => 0
case Active => 1
case Blocked => 2
case Defaulter => 3
}
private def fromInt(as: Int): AccountStatus = as match {
case 0 => Draft
case 1 => Active
case 2 => Blocked
case 3 => Defaulter
_ => sys.error("Out of bound AccountStatus Value.")
}
}
https://github.com/playframework/play-scala-slick-example/blob/2.6.x/app/models/Person.scala
答案 0 :(得分:2)
因此,需要在object AccountStatus
代码块的内添加此代码,因为我们需要使用fromInt
将Int
转换为AccountStatus
1}}。这是为AccountStatus定义的Reads
:
implicit object AccountStatusReads extends Reads[AccountStatus] {
def reads(jsValue: JsValue): JsResult[AccountStatus] = {
(jsValue \ "as").validate[Int].map(fromInt)
}
}
什么是Reads
?它只是一个trait
,它定义了如何将JsValue(封装JSON值的播放类)从JSON反序列化为某种类型。该特征只需要实现一个方法,reads
方法接受一些json并返回某种类型的JsResult
。因此,您可以在上面的代码中看到我们有一个Reads
,它将在JSON中查找名为as
的字段,并尝试将其作为整数读取。然后,它将使用已定义的fromInt方法将其转换为AccountStatus
。例如,在scala控制台中,您可以这样做:
import play.api.libs.json._
// import wherever account status is and the above reader
scala> Json.parse("""{"as":1}""").as[AccountStatus]
res0: AccountStatus = Active
这个读者并不完美,主要是因为它没有处理你的代码会给你带来超出数字的错误:
scala> Json.parse("""{"as":20}""").as[AccountStatus]
java.lang.RuntimeException: Out of bound AccountStatus Value.
at scala.sys.package$.error(package.scala:27)
at AccountStatus$.fromInt(<console>:42)
at AccountStatusReads$$anonfun$reads$1.apply(<console>:27)
at AccountStatusReads$$anonfun$reads$1.apply(<console>:27)
at play.api.libs.json.JsResult$class.map(JsResult.scala:81)
at play.api.libs.json.JsSuccess.map(JsResult.scala:9)
at AccountStatusReads$.reads(<console>:27)
at play.api.libs.json.JsValue$class.as(JsValue.scala:65)
at play.api.libs.json.JsObject.as(JsValue.scala:166)
... 42 elided
您可以通过使Reads
处理错误来解决此问题。我可以告诉你如果你想要,但首先Format
的另一部分是Writes
。 This trait,不出所料,与读取相似,只是反过来。您正在上课AccountStatus
并创建JsValue
(JSON)。因此,您只需要实现writes
方法。
implicit object AccountStatusWrites extends Writes[AccountStatus] {
def writes(as: AccountStatus): JsValue = {
JsObject(Seq("as" -> JsNumber(as.as)))
}
}
然后,这可用于将该类序列化为JSON,如下所示:
scala> Json.toJson(Draft)
res4: play.api.libs.json.JsValue = {"as":0}
现在,这实际上足以让您的错误消失。为什么?因为Json.format[Account]
正在做我们刚刚为你做的所有工作!但对于帐户。它可以这样做,因为它是一个案例类,并且少于22个字段。 同样 Account
的每个字段都有一种方式可以转换为JSON(来自Reads
和Writes
)。您的错误消息显示帐户无法自动为其创建格式,因为其中一部分(状态字段)没有格式化程序。
现在,为什么你必须这样做?由于AccountStatus
不是案例类,因此您无法在其上调用Json.format[AccountStatus]
。并且因为它的子类是每个对象,它们没有为它们定义unapply
方法,因为它们不是case类。因此,您必须向库解释如何序列化和反序列化。
既然你说你是scala的新手,我想隐含的概念仍然有些陌生。我建议你玩它/做一些阅读,以便在看到编译器抱怨无法找到它需要的隐含时,掌握该怎么做。
奖金回合
所以,你可能真的不想自己做这项工作,有一种方法可以避免这样做,所以你可以做Json.format[AccountStatus]
。您看到Json.format
使用apply
和unapply
方法执行其脏工作。在scala中,这两种方法是为case类自动定义的。但是没有理由你不能自己定义它们并获得它们免费提供给你的一切!
那么,apply
和unapply
看起来是什么样的签名呢?它每个类都会更改,但在这种情况下apply
应匹配Int => AccountStatus
(从int到AccountStatus的函数)。所以它定义如下:
def apply(i: Int): AccountStatus = fromInt(i)
和unapply类似于此相反,但它需要返回Option[Int]
,所以它看起来像
def unapply(as: AccountStatus): Option[Int] = Option(as.as)
定义了这两个,你不需要自己定义读写,而只需调用
// this is still inside the AccountStatus object { ... }
implicit val asFormat = Json.format[AccountStatus]
它将以类似的方式工作。
.P.S。我今天正在旅行,但如果其中一些没有意义,请随时留下任何评论,我会稍后再回复你