Play2 Scala:将 Json 反序列化为 objects 的列表

Play2 Scala: Deserialize Json into a List of objects

我正在尝试将使用 Play 的 WSClient 得到的 json body 响应反序列化到 objects 的列表中,我认为我不太接近从成功开始,但我可能还缺少最后一块拼图。

这些情况 类(和它们的同伴 objects)必须将传入的 json 反序列化为。

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]
}

公司雇主 DTO:

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)

}

我看到我的 WsClient 收到了负载:

但是当我尝试按如下方式反序列化响应时:response.json.as[CompanyEmployerCollectionDTO]

我收到这个错误:

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(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(ApiEmployeeClient.scala:33)
    at scala.util.Success.$anonfun$map(Try.scala:255)
    at scala.util.Success.map(Try.scala:213)
    at scala.concurrent.Future.$anonfun$map(Future.scala:292)
    at scala.concurrent.impl.Promise.liftedTree1(Promise.scala:33)

问题是光秃秃的and employerStoreDTOlistReads。因为您没有指定路径(至于其他 CompanyEmployerDTO 成员),解码器将尝试将当前 JSON 值本身(表示 CompanyEmployerDTO 的对象)解码为列表EmployerStoreDTO.

将这两个词替换为 and (JsPath \ "stores").read[List[EmployerStoreDTO]] 应该可以修复您的代码,但对于它的价值,您还可以通过取消 List 实例(将自动提供)来大大简化您的实现,删除导入等:

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)
}

这将执行与您的代码完全相同的操作,但更加简洁和易于管理。