Search code examples
scalaplayframeworktraitscase-classplay-json

Play JSON with sealed trait / case classes: infinite recursion


I have a code where I try to customize JSON serialization of a bunch of case classes by defining a custom Writes for the base trait. I'm getting infinite recursion / stack overflow.

I created a simplified sample - if somebody knows how to fix it, please let me know.

import play.api.libs.json._

sealed trait Person {
  val name: String
}

final case class Teacher(name: String, salary: Int) extends Person
final case class Student(name: String, grade: Int) extends Person

implicit val teacherWrites: Writes[Teacher] = Json.writes[Teacher]
implicit val studentWrites: Writes[Student] = Json.writes[Student]

val ThePersonWrites: Writes[Person] = Writes(person => {
  Json.writes[Person].writes(person).as[JsObject] - "_type"
})

implicit val personWrites: Writes[Person] = ThePersonWrites

val people = List[Person] (
  Teacher("Jane Doe", 40000),
  Student("Alice", 5),
  Student("Bob", 7)
  )

Json.prettyPrint(Json.toJson(people))

Solution

  • You need play-json-derived-codecs

    import play.api.libs.json._
    import julienrf.json.derived
    
    sealed trait Person {
      val name: String
    }
    
    object Person {
      implicit val jsonFormat: OFormat[Person] = derived.oformat[Person]()
    }
    
    final case class Teacher(name: String, salary: Int) extends Person
    final case class Student(name: String, grade: Int) extends Person
    
    
    val people = List[Person] (
      Teacher("Jane Doe", 40000),
      Student("Alice", 5),
      Student("Bob", 7)
    )
    
    println(Json.prettyPrint(Json.toJson(people)))
    

    See here the scalafiddle