Play Framework 2.8.2 - No Json serializer found for type (subclass)

老子叫甜甜 提交于 2021-01-07 05:58:26

问题


I'm migrating some of my services from Play 2.7.x to the newest 2.8.2, together with scala 2.13.2 and sbt 1.3.12.

I'm hitting an obstacle with the play-json though, and the Reads[A]

I have the following setup:

sealed trait Charge {}
case class ChargeOne(one: Int) extends Charge
case class ChargeTwo(two: Int) extends Charge

object Charge {
  implicit val writes: Writes[Charge] = (charge: Charge) => {...}
}

We have some tests looking like so

val chargeOne = ChargeOne(1)
val json = Json.toJson(charge)

json mustBe "..."

And these work fine in play 2.7.x with scala 2.12.x, and it does also work with scala 2.13.2, but after upgrading play to 2.8.2, the following error occurs when compiling:

No Json serializer found for type ChargeOne. Try to implement an implicit Writes or Format for this type.

And i have to add .asInstanceOf[Charge] to my tests to make it work.

What has happened here? Is it play-json? or is it scala? And does anyone know how to "fix" it?


回答1:


As indicated in the error message, an instance of Writes (or a Format) for ChargeOne is required, whereas the code only provide a Reads.

import play.api.libs.json._ // BTW Recommend to provide import in code examples

sealed trait Charge {}
case class ChargeOne(one: Int) extends Charge
case class ChargeTwo(two: Int) extends Charge

object Charge {
  implicit val format: OFormat[Charge] = {
    // Need to define instance for the subtypes (no auto-materialization)
    implicit def one = Json.format[ChargeOne]
    implicit def two = Json.format[ChargeTwo]

    Json.format[Charge]
  }
}

Then you can see that Writes and Reads are invariant (typeclasses) so only resolved for parent type Charge:

scala> Json.toJson(ChargeOne(1))
<console>:17: error: No Json serializer found for type ChargeOne. Try to implement an implicit Writes or Format for this type.
       Json.toJson(ChargeOne(1))
                  ^

scala> Json.toJson(ChargeOne(1): Charge)
res1: play.api.libs.json.JsValue = {"one":1,"_type":"ChargeOne"}

If you don't want to have to annotate on each toJson:

import play.api.libs.json._ // BTW Recommend to provide import in code examples

sealed trait Charge {}
case class ChargeOne(one: Int) extends Charge
case class ChargeTwo(two: Int) extends Charge

object Charge {
  implicit val format: OFormat[Charge] = {
    // Need to define instance for the subtypes (no auto-materialization)
    implicit def one = Json.format[ChargeOne]
    implicit def two = Json.format[ChargeTwo]

    Json.format[Charge]
  }

  implicit def genericWrites[T <: Charge]: OWrites[T] =
    format.contramap[T](c => c: Charge)

  implicit def genericReads[T <: Charge](implicit evidence: scala.reflect.ClassTag[T]): Reads[T] = format.collect[T](JsonValidationError(s"Type mismatch: ${evidence.runtimeClass.getName}")) {
    case `evidence`(t) => t
  }
}
    
scala> Json.toJson(ChargeOne(1))
res0: play.api.libs.json.JsValue = {"one":1,"_type":"ChargeOne"}

scala> Json.toJson(ChargeOne(1)).validate[Charge]
res0: play.api.libs.json.JsResult[Charge] = JsSuccess(ChargeOne(1),)

Note#1: It's important to see that (de)serialization ChargeOne (or any subtype) as Charge (or any similar parent type) to/from JSON is not the same as doing it "directly". The JSON representation is not the same as sealed family serialization requires a discriminator JSON field (see _type).

scala> Json.writes[ChargeOne].writes(ChargeOne(1))
res1: play.api.libs.json.JsObject = {"one":1}

scala> Json.toJson(ChargeOne(1)) // .. as Charge parent type
res2: play.api.libs.json.JsValue = {"one":1,"_type":"ChargeOne"}

Note#2: If some similar use cases were wrongly working in previous versions, it was leading to hardly predictable JSON representation, and critical implicit resolution in a lot of cases, which is the reason for this behavior change/fix (see pull request).



来源:https://stackoverflow.com/questions/62592017/play-framework-2-8-2-no-json-serializer-found-for-type-subclass

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!