简体   繁体   中英

Play2 Scala: Deserialize Json into a List of objects

I am trying to deserialize a json body response that I get using Play's WSClient into a list of objects and I think I'm not too close from succeeding, but there's probably the final piece of the puzzle that is missing me.

These are the case classes (with their companion objects) that the incoming json must be deserialized to.

EmployerStoreDTO:

import play.api.libs.json._

case class EmployerStoreDTO (id: String, storeName: String)

object EmployerStoreDTO {
  implicit val reads: Reads[EmployerStoreDTO] = Json.reads[EmployerStoreDTO]

  implicit val employerStoreDTOlistReads: Reads[List[EmployerStoreDTO]] = Reads.list[EmployerStoreDTO]
}

CompanyEmployerDTO:

import java.time.DayOfWeek

import play.api.libs.json._
import play.api.libs.json.Reads._
import play.api.libs.functional.syntax._

case class CompanyEmployerDTO(id: String,
                              companyName: String,
                              companyUrl: Option[String],
                              description: Option[String],
                              startDayOfWeek: DayOfWeek,
                              logoUrl: Option[String],
                              stores: List[EmployerStoreDTO] = List(),
                              supportHelpText: Option[String],
                              themeId: Option[String]) {
}

object CompanyEmployerDTO {

  import model.EmployerStoreDTO._

  implicit val startDayOfWeekReads: Reads[DayOfWeek] = (JsPath \ "weekStartDay").read[Int].map(DayOfWeek.of)

  implicit val reads: Reads[CompanyEmployerDTO] = (
    (JsPath \ "id").read[String] and
      (JsPath \ "companyName").read[String] and
      (JsPath \ "companyUrl").readNullable[String] and
      (JsPath \ "description").readNullable[String] and startDayOfWeekReads and
      (JsPath \ "logoUrl").readNullable[String] and employerStoreDTOlistReads and
      (JsPath \ "supportHelpText").readNullable[String] and
      (JsPath \ "themeId").readNullable[String]
    ) (CompanyEmployerDTO.apply _)

  implicit val companyEmployerDTOListReads: Reads[List[CompanyEmployerDTO]] = Reads.list[CompanyEmployerDTO]
}

CompanyEmployerCollectionDTO:

import play.api.libs.json.Reads._
import play.api.libs.json._

case class CompanyEmployerCollectionDTO (companies: List[CompanyEmployerDTO])

object CompanyEmployerCollectionDTO {

  implicit val reads: Reads[CompanyEmployerCollectionDTO] =
    (JsPath \ "companies").read[List[CompanyEmployerDTO]].map(CompanyEmployerCollectionDTO.apply)

}

I see that the payload is received by my WsClient : 在此处输入图片说明

But as I'm trying to deserialize the response as follows: response.json.as[CompanyEmployerCollectionDTO]

I get this error:

Caused by: play.api.libs.json.JsResultException: JsResultException(errors:List((/companies(0),List(JsonValidationError(List(error.expected.jsarray),WrappedArray())))))
    at play.api.libs.json.JsReadable.$anonfun$as$2(JsReadable.scala:25)
    at play.api.libs.json.JsError.fold(JsResult.scala:64)
    at play.api.libs.json.JsReadable.as(JsReadable.scala:24)
    at play.api.libs.json.JsReadable.as$(JsReadable.scala:23)
    at play.api.libs.json.JsObject.as(JsValue.scala:124)
    at services.com.mycompany.ApiEmployeeClient.$anonfun$callGetEmployers$2(ApiEmployeeClient.scala:33)
    at scala.util.Success.$anonfun$map$1(Try.scala:255)
    at scala.util.Success.map(Try.scala:213)
    at scala.concurrent.Future.$anonfun$map$1(Future.scala:292)
    at scala.concurrent.impl.Promise.liftedTree1$1(Promise.scala:33)

The problem is the bare and employerStoreDTOlistReads . Because you haven't specified a path (as for the other CompanyEmployerDTO members), the decoder will try to decode the current JSON value itself (an object representing the CompanyEmployerDTO ) as a list of EmployerStoreDTO .

Replacing those two words with and (JsPath \\ "stores").read[List[EmployerStoreDTO]] should fix your code, but for what it's worth you can also simplify your implementation quite a bit by scrapping the List instances (which will be provided automatically), dropping an import, etc.:

import play.api.libs.json._

case class EmployerStoreDTO (id: String, storeName: String)

object EmployerStoreDTO {
  implicit val reads: Reads[EmployerStoreDTO] = Json.reads[EmployerStoreDTO]
}

import java.time.DayOfWeek

import play.api.libs.json._
import play.api.libs.json.Reads._
import play.api.libs.functional.syntax._

case class CompanyEmployerDTO(id: String,
                              companyName: String,
                              companyUrl: Option[String],
                              description: Option[String],
                              startDayOfWeek: DayOfWeek,
                              logoUrl: Option[String],
                              stores: List[EmployerStoreDTO] = List(),
                              supportHelpText: Option[String],
                              themeId: Option[String]) {
}

object CompanyEmployerDTO {
  implicit val reads: Reads[CompanyEmployerDTO] = (
    (JsPath \ "id").read[String] and
      (JsPath \ "companyName").read[String] and
      (JsPath \ "companyUrl").readNullable[String] and
      (JsPath \ "description").readNullable[String] and
      (JsPath \ "weekStartDay").read[Int].map(DayOfWeek.of) and
      (JsPath \ "logoUrl").readNullable[String] and
      (JsPath \ "stores").read[List[EmployerStoreDTO]] and
      (JsPath \ "supportHelpText").readNullable[String] and
      (JsPath \ "themeId").readNullable[String]
    ) (CompanyEmployerDTO.apply _)
}

import play.api.libs.json.Reads._
import play.api.libs.json._

case class CompanyEmployerCollectionDTO (companies: List[CompanyEmployerDTO])

object CompanyEmployerCollectionDTO {
  implicit val reads: Reads[CompanyEmployerCollectionDTO] =
    (JsPath \ "companies").read[List[CompanyEmployerDTO]].map(CompanyEmployerCollectionDTO.apply)
}

This will do exactly the same thing as your code, but is a little more concise and manageable.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM