You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Scala中从两个结构不同的API响应JSON构建Seq[Appointment]

如何在Scala中从两个结构不同的API响应JSON构建Seq[Appointment]

嘿,我来帮你搞定这个兼容两个API的问题!你既要保留现有的Appointment类和已经在运行的JSON格式化逻辑,又要能从另一个结构完全不同的API响应里解析出Seq[Appointment]对吧?咱们一步步来实现:

首先,绝对不碰你现有的核心代码——那个自动生成的Json.format[Appointment]必须原封不动保留,毕竟不能破坏已经跑起来的功能,它专门负责处理第一个和类结构完全一致的API响应。

接下来,咱们要为第二个API的结构写一个自定义的Reads规则,用来把它的字段映射到你的Appointment类上。看第二个API的JSON,字段名、类型都有差异:比如id是数字类型要转成String的Option,establishment对应你的entity,id_client对应beneficiary,date_start/date_end对应start_at/end_at,还有一些字段第二个API没有,直接设为None就行:

object Appointment {
  // 保留原有的格式化逻辑,完全兼容第一个API的现有代码
  implicit val jsonFormat: OFormat[Appointment] = Json.format[Appointment]

  // 专门适配第二个API的单个Appointment解析规则
  private val secondApiAppointmentReads: Reads[Appointment] = (
    // 把Int类型的id转成String的Option
    (JsPath \ "id").read[Int].map(id => Some(id.toString)) and
    // 直接读取service字段,允许为null
    (JsPath \ "service").readNullable[String] and
    // date_start映射到start_at
    (JsPath \ "date_start").readNullable[String] and
    // date_end映射到end_at
    (JsPath \ "date_end").readNullable[String] and
    // establishment映射到entity
    (JsPath \ "establishment").readNullable[String] and
    // 第二个API没有status字段,直接设为None
    Reads.pure(None) and
    // 把Int类型的id_client转成String的Option,对应beneficiary
    (JsPath \ "id_client").read[Int].map(clientId => Some(clientId.toString))
  )(Appointment.apply _)

  // 通用的Seq[Appointment]解析器,自动适配两种API结构
  implicit val seqAppointmentReads: Reads[Seq[Appointment]] = 
    // 先尝试解析第一种情况:直接是Appointment的JSON数组(第一个API的响应)
    Reads.seq(jsonFormat)
      .orElse(
        // 如果第一种解析失败,尝试第二种:从data字段里解析数组(第二个API的响应)
        (JsPath \ "data").read(Reads.seq(secondApiAppointmentReads))
      )
}

现在你就可以用这个通用解析器处理任意一个API的响应了:不管是第一个API返回的纯Appointment数组,还是第二个API返回的包裹在data字段里的数组,调用responseJson.validate[Seq[Appointment]]都会自动匹配对应的解析规则,完美兼容两种情况,而且完全不会影响原来使用jsonFormat的代码。

举个实际使用的例子:

// 假设response是从API拿到的JsValue
val result: JsResult[Seq[Appointment]] = response.validate[Seq[Appointment]]
result match {
  case JsSuccess(appointments, _) => // 处理成功拿到的预约列表
  case JsError(errors) => // 处理解析错误
}

这样就搞定啦,既保留了现有实现,又支持了新的API结构,一举两得!

备注:内容来源于stack exchange,提问作者ahabrahante

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.23 10:09:04