为具有泛型类型类的案例类播放 JSON 格式化程序
Posted
技术标签:
【中文标题】为具有泛型类型类的案例类播放 JSON 格式化程序【英文标题】:Play JSON formatter for case class with generic type classes 【发布时间】:2016-12-01 20:29:21 【问题描述】:正在尝试为这个案例类编写一个 json 格式化程序
case class OptionRange[+T](start: Option[T], end: Option[T])
这是我目前所拥有的
implicit def fmt[T <: OptionRange[_]](implicit fmt: Format[Option[T]]): Format[OptionRange[T]] = new Format[OptionRange[T]]
def reads(json: JsValue): JsSuccess[OptionRange[T]] = JsSuccess(new OptionRange[T] (
(json \ "start").as[Option[T]],
(json \ "end").as[Option[T]]
))
def writes(i: OptionRange[T]) = JsObject(Seq(
"start" -> Json.toJson(i.start),
"end" -> Json.toJson(i.end)
))
这段代码可以编译,但是当我尝试格式化 OptionRange[Int]
时,我收到一个错误,提示没有可用的隐式格式。
我怎样才能编写一个在隐式范围内可用的格式?
【问题讨论】:
请注意,.as[Option[T]]
不是在 Play-Json 中执行此操作的惯用方式。 .readNullable[T]
更可取。它会自动在Option
中提升您的T
其实@LouisF。 .readNullable[T]
是 JsPath
上可用的方法,而不是 JsValue
... 请在下面查看我的答案
【参考方案1】:
您正在进行一些循环类型定义。
在我看来,在这种情况下,您打算将 T
评估为 Int
。但是,在函数定义中,您已约束 [T <: OptionRange[_]]
。所以,Scala 认为T
一定是某个东西的OptionRange
。
当您转到函数的隐式参数(implicit fmt: Format[Option[T]])
时,这会变得更加复杂。如果T
是OptionRange[_]
,那么您是在告诉编译器需要Format[Option[OptionRange[_]]]
而不是Format[Option[_]]
。您的函数不能是该 Format
的来源,因为无法对其进行评估以提供所需的隐式。
解决办法是停止约束T
。
implicit def fmt[T](implicit fmt: Format[Option[T]]) ...
然后,当您尝试将OptionRange
格式化为 json 时:
scala> import play.api.libs.json._
import play.api.libs.json._
scala> case class OptionRange[+T](start: Option[T], end: Option[T])
defined class OptionRange
scala> implicit def fmt[T](implicit fmt: Format[Option[T]]): Format[OptionRange[T]] = new Format[OptionRange[T]]
| def reads(json: JsValue): JsSuccess[OptionRange[T]] = JsSuccess(new OptionRange[T] (
| (json \ "start").as[Option[T]],
| (json \ "end").as[Option[T]]
| ))
| def writes(i: OptionRange[T]) = JsObject(Seq(
| "start" -> Json.toJson(i.start),
| "end" -> Json.toJson(i.end)
| ))
|
fmt: [T](implicit fmt: play.api.libs.json.Format[Option[T]])play.api.libs.json.Format[OptionRange[T]]
scala> Json.toJson(OptionRange(Some(1), Some(2)))
res0: play.api.libs.json.JsValue = "start":1,"end":2
【讨论】:
您能告诉我您在控制台中输入的所有内容吗?我无法复制你能做到的。另外,你用的是哪个版本的 play-json? @adamtrousdale 我相信我正在使用 v2.3.8 的 play-json。但是,这在这里不应该相关。 @adamtrousdale 另外,我更新了我的答案,让我在 scala 控制台中输入了代码。 我在控制台中没有得到相同的结果,我得到了scala> Json.toJson(OptionRange(Some(1), Some(2))) <console>:22: error: No Json serializer found for type OptionRange[Int]. Try to implement an implicit Writes or Format for this type. Json.toJson(OptionRange(Some(1), Some(2)))
您能否以某种方式准确地告诉我您在每次输入后在 scala 控制台中输入和看到的内容?您是否尝试过手动指定 fmt
而不将其留给隐式解析?【参考方案2】:
这是一种使用 play-framework 2.3.9
、Scala 2.11.8
的方法:
import play.api.libs.json._ // JSON library
import play.api.libs.json.Reads._ // Custom validation helpers
import play.api.libs.functional.syntax._ // Combinator syntax
case class OptionRange[+T](start: Option[T], end: Option[T])
object Hello extends App
implicit def formatOptionRange[T](implicit formatT: Format[T]): Format[OptionRange[T]] =
(
(JsPath \ "start").formatNullable[T] and
(JsPath \ "end").formatNullable[T]
)(OptionRange.apply, unlift(OptionRange.unapply))
println(Json.toJson(OptionRange(Some(1), Some(2))))
//Prints:
//"start":1,"end":2
这是format 上的文档。
【讨论】:
以上是关于为具有泛型类型类的案例类播放 JSON 格式化程序的主要内容,如果未能解决你的问题,请参考以下文章