使用 circe 时如何在 Scala 中表示动态 JSON 键
Posted
技术标签:
【中文标题】使用 circe 时如何在 Scala 中表示动态 JSON 键【英文标题】:How to represent dynamic JSON keys in Scala when using circe 【发布时间】:2019-06-03 05:48:57 【问题描述】:我正在尝试将以下 JSON 表示为 Scala 案例类:
"cars":
"THIS IS A DYNAMIC KEY 1":
"name": "bla 1",
,
"THIS IS A DYNAMIC KEY 2":
"name": "bla 2",
...
但是,我的 JSON 具有我在运行时不知道的动态键,我想使用 circe 进行编码/解码。我正在使用正确的方式来使用 Scala 来表示它?
import io.circe.generic.JsonCodec
@JsonCodec
case class Cars(cars: List[Car])
@JsonCodec
case class Car(whatShouldThisBe: CarDetails) // Not sure how to represent this?
@JsonCodec
case class CarDetails(name: String)
【问题讨论】:
如何“动态”是关键?来自一组/有限的可能性列表或“任何有效字符串”?如果是前者,您可以使用sealed trait
和几个实现。如果是后者,则将Car
建模为一个元组或将Cars
作为一个整体建模为Map
。
谢谢,密钥可以是任何有效的字符串
【参考方案1】:
我认为您可以使用Map[String, CarDetails]
。然后您的 ADT 变为:
import io.circe.generic.JsonCodec
@JsonCodec
case class Cars(cars: Map[String, CarDetails])
@JsonCodec
case class CarDetails(name: String)
唯一棘手的地方可能是您要求至少有一个 CarDetails 对象,或者是否可以接受零。如果需要的话,Circe 似乎确实支持cats.data.NonEmptyMap
。
【讨论】:
赞成,如果没有特别的理由需要Car
案例类级别,这是最好的解决方案。【参考方案2】:
处理此类案例的最直接方法可能是将Cars
案例类的cars
成员更改为具有类似Map[String, CarDetails]
的类型,完全删除Car
案例类。如果您这样做,您的代码将完全按原样工作(减去 Car
定义),并将解码您提供的 JSON 示例。
如果您想要更接近案例类结构的内容,可以执行以下操作:
import io.circe.Decoder
import io.circe.generic.JsonCodec
case class Cars(cars: List[Car])
object Cars
implicit val decodeCars: Decoder[Cars] =
Decoder[Map[String, CarDetails]].prepare(_.downField("cars")).map(kvs =>
Cars(
kvs.map
case (k, v) => Car(k, v)
.toList
)
)
// I've added an `id` member here as a way to hold on to the JSON key.
case class Car(id: String, whatShouldThisBe: CarDetails)
@JsonCodec
case class CarDetails(name: String)
这将解码相同的 JSON,但会在 Car
级别中包含动态键。
【讨论】:
以上是关于使用 circe 时如何在 Scala 中表示动态 JSON 键的主要内容,如果未能解决你的问题,请参考以下文章