使用 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 键的主要内容,如果未能解决你的问题,请参考以下文章

在Scala中表示readline循环的最佳方法?

如何在 GADT 中表示此 FSM

如何在circe中编码/解码json的时间戳?

我将如何在 Scala 中表达链式赋值?

使用 circe 在 Scala 中 JSON 将嵌套字段解码为 Map[String, String]

Scala使用circe将None编码为NaN json值