具有递归类定义的 Json 隐式格式

Posted

技术标签:

【中文标题】具有递归类定义的 Json 隐式格式【英文标题】:Json implicit format with recursive class definition 【发布时间】:2015-08-23 02:21:40 【问题描述】:

我定义了一个递归类:

case class SettingsRepository(id: Option[BSONObjectID],
                          name: Option[String],
                          children: Option[List[SettingsRepository]])

JSON 隐式格式如下:

implicit val repositoryFormat = Json.format[SettingsRepository]

我该如何解决这个编译错误? :

No implicit format for Option[List[models.practice.SettingsRepository]] available.
In /path/to/the/file.scala:95

95 implicit val repositoryFormat = Json.format[SettingsRepository] 

我试图定义一个惰性读取/写入/格式包装器,但没有成功... 有人知道这样做的干净方法吗?

【问题讨论】:

【参考方案1】:

正如您所发现的,您不能在此处使用 JSON inception 宏,但您可以编写自己的 Format(请注意,为了完整工作,我已将 BSONObjectID 替换为 Long示例):

import play.api.libs.functional.syntax._
import play.api.libs.json._

case class SettingsRepository(
  id: Option[Long],
  name: Option[String],
  children: Option[List[SettingsRepository]]
)

implicit val repositoryFormat: Format[SettingsRepository] = (
  (__ \ 'id).formatNullable[Long] and
  (__ \ 'name).formatNullable[String] and
  (__ \ 'children).lazyFormatNullable(implicitly[Format[List[SettingsRepository]]])
)(SettingsRepository.apply, unlift(SettingsRepository.unapply))

诀窍是提供显式类型注释并使用implicitly 而不仅仅是lazyFormatNullable 上的类型参数。

【讨论】:

【参考方案2】:

对于其他来这里寻找轻微变体的人,我们在 Format 类中覆盖 readswrites(如 API 文档中的 example given),您可以声明对所需对象的惰性引用:

  lazy val tweetFormat: Format[Tweet] = TweetFormat
  implicit object UserFormat extends Format[User] 
  //...
  

  //...
  implicit object TweetFormat extends Format[Tweet] 
  //...

【讨论】:

【参考方案3】:

只是一个更新。这已经修复了很长时间。例如,在play-json 2.3.9 版本中,这是Maven 上最旧的版本,以下工作:

import play.api.libs.json._

case class SettingsRepository(id: Option[Int],
                              name: Option[String],
                              children: Option[List[SettingsRepository]])

implicit val repositoryFormat = Json.format[SettingsRepository]

val s = SettingsRepository(Some(2), Some("name"), Some(List(SettingsRepository(Some(1), Some(""), None))))
println(Json.toJsObject(s))

和输出:

"id":2,"name":"name","children":["id":1,"name":""]

代码在Scastie 运行。

【讨论】:

以上是关于具有递归类定义的 Json 隐式格式的主要内容,如果未能解决你的问题,请参考以下文章

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

在子类中重用 json 隐式读取器

使用 boost::mutex - 隐式删除错误(因为默认定义格式不正确)

Firebase 类型和具有默认 tslint.json 的无隐式依赖项

创建一个自定义属性,提示 Resharper 隐式使用该属性

为啥构造函数总是与类具有相同的名称以及它们是如何被隐式调用的?