Home > Net >  Diverging Implicit Error When Parsing JSON to Case Class in Scala
Diverging Implicit Error When Parsing JSON to Case Class in Scala

Time:12-08

I'm facing a strange error where I'm trying to parse a JSON String into a generic case class. My case class looks like this:

final case class LocationAPIObject[F[_]](
  countryCode: F[String],
  partyId: F[String],
  uid: F[String]
)

For a JSON that comes in like this:

  val js: JsValue = Json.parse("""{
    "countryCode": "us",
    "partyId": "123456"
  }""")

It results in a parse error:

diverging implicit expansion for type play.api.libs.json.Reads[T]
starting with method Tuple22R in trait GeneratedReads

Here is the sample code that I'm working on: https://scastie.scala-lang.org/aBQIUCTvTECNPgSjCjDFlw

CodePudding user response:

You have diverging implicit expansion error because you haven't specified type parameter. If you do

val locationAPI = Json.fromJson[LocationAPIObject[Option]](js)

then the error changes to implicit not found: play.api.libs.json.Reads[LocationAPIObject[scala.Option]]. No Json deserializer found for type LocationAPIObject[Option]. Try to implement an implicit Reads or Format for this type.

The case class LocationAPIObject being parametrized with higher-kinded F ("fields which are encapsulated effects") shouldn't be a problem. The fields are of type F[String] so in order to derive instances of the type class Reads or Format for LocationAPIObject[F] it should be enough to know instances for F[String].

But while this works for example in Circe

import io.circe.Decoder
import io.circe.parser.decode
import io.circe.generic.semiauto

implicit def locDec[F[_]](implicit ev: Decoder[F[String]]): Decoder[LocationAPIObject[F]] =
  semiauto.deriveDecoder[LocationAPIObject[F]]

decode[LocationAPIObject[Option]](
  """{
    "countryCode": "us",
    "partyId": "123456"
  }"""
)
// Right(LocationAPIObject(Some(us),Some(123456),None))

for some reason it doesn't in Play json:

implicit def locFormat[F[_]](implicit ev: Format[F[String]]): Format[LocationAPIObject[F]] =
  Json.format[LocationAPIObject[F]]

or

implicit def locFormat[F[_]](implicit ev: OFormat[F[String]]): OFormat[LocationAPIObject[F]] =
  Json.format[LocationAPIObject[F]]
// No instance of play.api.libs.json.Format is available for F, F, F in the implicit scope (Hint: if declared in the same file, make sure it's declared before)

or

implicit def locReads[F[_]](implicit ev: Reads[F[String]]): Reads[LocationAPIObject[F]] =
  Json.reads[LocationAPIObject[F]]
// No instance of play.api.libs.json.Reads is available for F, F, F in the implicit scope (Hint: if declared in the same file, make sure it's declared before)

So the thing seems to be in Play json derivation macros.

The easiest would be to define a codec manually as it was adviced in the comments.

  • Related