0
votes

I would like to create a JSON reads converter with type parameter like this:



case class AClass(name: String, age: Int)
case class AClass(name: String)

object GeneralReadsType extends Enumeration {
  type GeneralReadsType = Value
  val AReadType = Value("atype")
  val BReadType = Value("btype")
}

implicit val repoReads: Reads[GeneralReadsType.GeneralReadsType] = {

  GeneralReadsType match {
    case GeneralReadsType.AReadType => (
      (JsPath \ "name").read[String] and
        (JsPath \ "age").read[Int]
      ) (GeneralReadsType.AReadType.apply _)


    case GeneralReadsType.BReadType => (
      (JsPath \ "name").read[String]
      ) (GeneralReadsType.BReadType.apply _)

  }
}

But when I try to use it in

def getResponse[GeneralReadsType](request: WSRequest): Future[List[GeneralReadsType]] = {
      request.get().map {
        case response if response.status == 200 => {
          (response.json).validate[List[GeneralReadsType]] match {
            case JsSuccess(items: List[GeneralReadsType], _) => items
            case _: JsError => List[GeneralReadsType]()
          }
        }
        case _ => List[GeneralReadsType]()
      }
    }


getResponse[AReadType](request)

I get a compile error

No Json deserializer found for type List[GeneralReadsType]. Try to implement an implicit Reads or Format for this type.

So, how should this be achieved?

Thanks!

1
Make sure your instance is in the implicit scope. Alsoif your are testing in REPL, the behaviour is not exactly the same, so take care. - cchantep
You defined a Reads for GeneralReadsType.GeneralReadsType but getResponse needs a Reads for GeneralReadsType. - Dylan

1 Answers

0
votes

I find it best to place format objects as an implicit field in the object itself (or in companion object of a case class). Also when using Enumeration plural form for the Enumeration object and singular for the value make it read better. So:

object GeneralReadsTypes extends Enumeration {
  type GeneralReadsType = Value
  val AReadType = Value("atype")
  val BReadType = Value("btype")

  implicit val repoReads: Reads[GeneralReadsType] = {
    ((JsPath \ "name").read[String] and
      (JsPath \ "age").readNullable[Int]).apply { (_, maybeAge) =>
      maybeAge.fold(BReadType)( _ => AReadType)
    }
  }  
}

or if you find functional style too foreign:

object GeneralReadsTypes extends Enumeration {
  type GeneralReadsType = Value
  val AReadType = Value("atype")
  val BReadType = Value("btype")

  implicit val repoReads: Reads[GeneralReadsType] = {
    ((JsPath \ "name").read[String] and
      (JsPath \ "age").readNullable[Int]).apply { (_, maybeAge) =>
      if (maybeAge.isDefined) AReadType
      else BReadType
    }
  }
}