为具有泛型类型类的案例类播放 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 &lt;: OptionRange[_]]。所以,Scala 认为T 一定是某个东西的OptionRange

当您转到函数的隐式参数(implicit fmt: Format[Option[T]]) 时,这会变得更加复杂。如果TOptionRange[_],那么您是在告诉编译器需要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&gt; Json.toJson(OptionRange(Some(1), Some(2))) &lt;console&gt;: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 格式化程序的主要内容,如果未能解决你的问题,请参考以下文章

MyBatis里json型字段到Java类的映射

使用密封的特征/案例类播放JSON:无限递归

spray-json 找不到 List[T] 类型的 JsonReader

笔记:泛型

C#实体类中如何定义泛型集合类型的属性?

Java 泛型