英文:
Take data from two different Json files to create an Seq[Object] in Scala
问题
我有一个类模型如下:
```scala
final case class Appointment(id: Option[String] = None,
service: Option[String] = None,
start_at: Option[String] = None,
end_at: Option[String] = None,
entity: Option[String] = None,
status: Option[String] = None,
beneficiary: Option[String] = None,
)
我从两个不同的API获得响应,它们提供具有不同结构的JSON。第一个与我的类具有相同的结构,因此获取其数据足够使用以下内容:
object Appointment {
implicit val jsonFormat: OFormat[Appointment] = Json.format[Appointment]
}
但是第二个不同,如下所示:
{
data[
{
"id": 101643,
"establishment": "some stablishment",
"id_client": 125,
"reservation_date": "2023-01-23",
"date_start": "2023-01-23T05:00:00.000000Z",
"date_end": "2023-01-23T05:00:00.000000Z",
"service": "some service",
}]
}
这里 我找到了一个解决方案,当它是这样或那样的时候,但我需要一种结合这两种方法的方式。我不能更改我的类属性,也不能这样做:
object User {
implicit val jsonReads: Reads[User] = (
(JsPath \ "id").read[String] and
(JsPath \ "username").read[String] and
(JsPath \ "profile_picture").read[String] and
(JsPath \ "full_name").read[String]
)(User.apply _)
}
因为这将破坏已经存在的实现。
注意 我需要 Seq[Appointment]
总结
我需要创建一个 Seq[Appointment]
,无论是使用来自第一个API的信息还是使用来自第二个API的信息,这两个API都提供具有不同结构的响应,而第二个API提供的结构与我的 class Appointment
模型不同。
<details>
<summary>英文:</summary>
I have a class model like this
```scala
final case class Appointment(id: Option[String] = None,
service: Option[String] = None,
start_at: Option[String] = None,
end_at: Option[String] = None,
entity: Option[String] = None,
status: Option[String] = None,
beneficiary: Option[String] = None,
)
I get responses from two diferent APIs that give JSONs with different structure, the first one have the same structure as my class, so to get it's data it's enough with this:
object Appointment {
implicit val jsonFormat: OFormat[Appointment] = Json.format[Appointment]
}
But the second one it's different like this:
{
data[
{
"id": 101643,
"establishment": "some stablishment",
"id_client": 125,
"reservation_date": "2023-01-23",
"date_start": "2023-01-23T05:00:00.000000Z",
"date_end": "2023-01-23T05:00:00.000000Z",
"service": "some service",
}]
}
Here I found a solution when it's one way or another but I need a way to combine both methods. I can't change my class attributes and also can't do this:
object User {
implicit val jsonReads: Reads[User] = (
(JsPath \ "id").read[String] and
(JsPath \ "username").read[String] and
(JsPath \ "profile_picture").read[String] and
(JsPath \ "full_name").read[String]
)(User.apply _)
}
because I will brake the implementation already existing.
Note I need Seq[Appointment]
# Summarizing
I need create a Seq[Appointment]
either with the info gived to from the first API or from the second, both APIs give responsens with different structures, and the second one give a structure different to my class Appointment
model
答案1
得分: 0
利用 [`orElse`](https://www.playframework.com/documentation/2.8.x/api/scala/play/api/libs/json/Reads.html#orElse(v:play.api.libs.json.Reads[A]):play.api.libs.json.Reads[A]) 方法。
这是`Reads`特征上的一个方法,它允许你将多个`Reads`组合成一种类似“备用”链的形式。如果第一个失败,它会尝试第二个。
为要支持的每种JSON格式创建单独的`Reads`,然后使用`orElse`将它们组合在一起,创建一个支持所有格式的单一`Reads`。
具体操作方式将取决于你的类的设计方式。例如,它们是否共享一个公共超类型?它们是否完全独立,但可以从一种类型转换为另一种类型?
例如,你可以这样做:
```scala
implicit val theOtherTypeReads: Reads[TheOtherAppointmentType] = /* ... */
implicit val appointmentReads: Reads[Appointment] = {
val defaultReads = Json.reads[Appointment]
val otherTypeReads = theOtherTypeReads.map(_.convertToAppointment)
defaultReads.orElse(otherTypeReads)
}
或者
implicit val appointmentReads: Reads[Appointment] = {
val subtype1Reads = Json.reads[AppointmentSubtype1]
val subtype2Reads = Json.reads[AppointmentSubtype2]
subtype1Reads orElse subtype2Reads
// 注意,如果`Appointment`是一个密封特征,`Json.reads`宏可能已经为你执行了此操作...
}
<details>
<summary>英文:</summary>
Take advantage of [`orElse`](https://www.playframework.com/documentation/2.8.x/api/scala/play/api/libs/json/Reads.html#orElse(v:play.api.libs.json.Reads[A]):play.api.libs.json.Reads[A])
It's a method on the `Reads` trait which allows you to combine multiple `Reads` in a sort of "fallback" chain. If the first one fails, it tries the second one.
Make a separate `Reads` for each JSON format you want to support, then `orElse` them together to create a single `Reads` that supports all formats.
The details of doing so will depend on how your classes are designed. I.e. do they share a common supertype? Are they completely separate, but have a conversion from one type to the other?
For example you might do
```scala
implicit val theOtherTypeReads: Reads[TheOtherAppointmentType] = /* ... */
implicit val appointmentReads: Reads[Appointment] = {
val defaultReads = Json.reads[Appointment]
val otherTypeReads = theOtherTypeReads.map(_.convertToAppointment)
defaultTypeReads.orElse(otherTypeReads)
}
or
implicit val appointmentReads: Reads[Appointment] = {
val subtype1Reads = Json.reads[AppointmentSubtype1]
val subtype2Reads = Json.reads[AppointmentSubtype2]
subtype1Reads orElse subtype2Reads
// note, the Json.reads macro might already do this for you,
// if `Appointment` is a sealed trait...
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论