1

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

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
joesan
  • 13,963
  • 27
  • 95
  • 232
  • Your class contains fields which are encapsulated effects. I don't think it should be possible to get an auto derived Codec for this. – sarveshseri Dec 06 '22 at 09:51
  • If you are opened for using other JSON parsers then try jsoniter-scala. It is much handy in derivation and [supports parsing and serialization of higher-kinded types out of the box](https://github.com/plokhotnyuk/jsoniter-scala/blob/02169c801d9e21ca426115a8e71134de262ab02e/jsoniter-scala-macros/shared/src/test/scala/com/github/plokhotnyuk/jsoniter_scala/macros/JsonCodecMakerSpec.scala#L2323-L2333). Also, it is [much more efficient in runtime](https://plokhotnyuk.github.io/jsoniter-scala/). – Andriy Plokhotnyuk Dec 06 '22 at 11:28
  • 1
    Check this updated [scastie](https://scastie.scala-lang.org/bhIj4nQYRqO3zTdwqUNtgw). – Johny T Koshy Dec 06 '22 at 12:17
  • Thanks for the help. More or less what I wanted! – joesan Dec 06 '22 at 15:01
  • 1
    Note that Andriy Plokhotnyuk is promoting his own library, so this is not an unbiased recommendation. – Tim Dec 06 '22 at 18:28
  • @sarveshseri Encapsulated effect shouldn't be a problem. This type-class derivation works for example in Circe (see my answer) but doesn't for some reason in Play json. The issue is maybe in Play json derivation macros. – Dmytro Mitin Dec 07 '22 at 15:20
  • @DmytroMitin Yes, as long as we restrict the `F` to be such that `F[A]` is serializable for any serializable `A`. Or in other words, it must be possible to derive a `Decoder[F[A]]` given a `Decoder[A]`. – sarveshseri Dec 08 '22 at 11:16

1 Answers1

2

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.

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66