6

I have a recursive class defined :

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

with a JSON implicit format as below :

implicit val repositoryFormat = Json.format[SettingsRepository]

How can I do to resolve this compilation error? :

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

95 implicit val repositoryFormat = Json.format[SettingsRepository] 

I tried to define a lazy reads/write/format wrapper without any success... Anyone know a clean way to do that?

Tomer Shetah
  • 8,413
  • 7
  • 27
  • 35
Xan
  • 65
  • 6

3 Answers3

9

As you've discovered, you can't use the JSON inception macro here, but you can write your own Format (note that I've replaced BSONObjectID with Long for the sake of a complete working example):

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))

The trick is providing an explicit type annotation and using implicitly rather than just a type parameter on lazyFormatNullable.

Travis Brown
  • 138,631
  • 12
  • 375
  • 680
1

For others who came here looking for a slight variant where we override reads and writes in the Format class (like the example given on the API docs), you can declare a lazy reference to your needed object:

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

  //...
  implicit object TweetFormat extends Format[Tweet] {
  //...
bbarker
  • 11,636
  • 9
  • 38
  • 62
1

Just an update. This is already fixed for a long time. For example, in play-json version 2.3.9, which is the oldest available at Maven, the following works:

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))

and outputs:

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

Code run at Scastie.

Tomer Shetah
  • 8,413
  • 7
  • 27
  • 35