Rav*_*yal 3 json scala playframework
隐式作用域中的 models.AccountStatus 没有可用的 play.api.libs.json.Format 实例。
这是取自 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.")
}
}
Run Code Online (Sandbox Code Playgroud)
https://github.com/playframework/play-scala-slick-example/blob/2.6.x/app/models/Person.scala
因此,需要将此代码添加到代码块内部object AccountStatus
,因为我们需要使用fromInt
将 an 转换Int
为 an AccountStatus
。这是Reads
为 AccountStatus 定义的:
implicit object AccountStatusReads extends Reads[AccountStatus] {
def reads(jsValue: JsValue): JsResult[AccountStatus] = {
(jsValue \ "as").validate[Int].map(fromInt)
}
}
Run Code Online (Sandbox Code Playgroud)
什么是Reads
?它只是trait
定义了如何将 JsValue(封装 JSON 值的播放类)从 JSON 反序列化为某种类型。该特征只需要实现一个方法,该reads
方法接受一些 json 并返回JsResult
某种类型的 a。因此,您可以在上面的代码中看到,我们有一个Reads
将在 JSON 中查找名为的字段as
,并尝试将其作为整数读取。然后,它会将其转换为AccountStatus
使用已定义的 fromInt 方法。例如,在 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
Run Code Online (Sandbox Code Playgroud)
不过,这个阅读器并不完美,主要是因为它没有处理代码在超出范围的数字时给您带来的错误:
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
Run Code Online (Sandbox Code Playgroud)
您可以通过处理Reads
错误来处理这个问题。如果您愿意,我可以向您展示如何操作,但首先 a 的另一部分Format
是 a Writes
。不出所料,这个特性与读取类似,只是它的作用相反。您正在上课AccountStatus
并创建一个JsValue
(JSON)。因此,您只需实现该writes
方法即可。
implicit object AccountStatusWrites extends Writes[AccountStatus] {
def writes(as: AccountStatus): JsValue = {
JsObject(Seq("as" -> JsNumber(as.as)))
}
}
Run Code Online (Sandbox Code Playgroud)
然后可以使用该类将该类序列化为 JSON,如下所示:
scala> Json.toJson(Draft)
res4: play.api.libs.json.JsValue = {"as":0}
Run Code Online (Sandbox Code Playgroud)
现在,这实际上足以让您的错误消失。为什么?因为Json.format[Account]
我们刚刚为您完成了所有工作!但对于帐户。它可以做到这一点,因为它是一个案例类并且具有少于 22 个字段。此外,每个字段Account
都有一种与 JSON 相互转换的方法(通过 aReads
和Writes
)。您的错误消息显示帐户无法自动为其创建格式,因为其中一部分(状态字段)没有格式化程序。
现在,你为什么必须这样做?因为AccountStatus
不是案例类,所以不能调用Json.format[AccountStatus]
它。因为它的子类都是对象,没有unapply
为它们定义方法,因为它们不是案例类。所以你必须向库解释如何序列化和反序列化。
既然你说你是scala的新手,我想隐式的概念仍然有点陌生。我建议您尝试一下/做一些阅读,以了解当您看到编译器抱怨无法找到它需要的隐式内容时该怎么做。
奖金轮
因此,您可能真的不想自己做这项工作,并且有一种方法可以避免必须这样做,这样您就可以做Json.format[AccountStatus]
。您会看到Json.format
使用apply
和unapply
方法来完成其肮脏的工作。在scala中,这两个方法是为案例类自动定义的。但您没有理由不能自己定义它们并免费获得它们为您提供的一切!
那么,apply
和unapply
看起来像类型签名吗?它会根据类而变化,但在本例中apply
应该匹配Int => AccountStatus
(从 int 到 AccountStatus 的函数)。所以它的定义如下:
def apply(i: Int): AccountStatus = fromInt(i)
Run Code Online (Sandbox Code Playgroud)
和 unapply 与此相反类似,但它需要返回一个Option[Int]
,所以它看起来像
def unapply(as: AccountStatus): Option[Int] = Option(as.as)
Run Code Online (Sandbox Code Playgroud)
通过这两个定义,您不需要自己定义读取和写入,而只需调用
// this is still inside the AccountStatus object { ... }
implicit val asFormat = Json.format[AccountStatus]
Run Code Online (Sandbox Code Playgroud)
它将以类似的方式工作。
.PS 我今天要去旅行,但如果其中一些内容没有意义,请随时留下任何评论,我稍后会尽力回复您
归档时间: |
|
查看次数: |
8207 次 |
最近记录: |