2 votes

Comment représenter des clés JSON dynamiques en Scala avec circe ?

J'essaie de représenter le JSON suivant comme une classe de cas Scala :

     {
       "cars": {
          "THIS IS A DYNAMIC KEY 1": {
            "name": "bla 1",
          },
          "THIS IS A DYNAMIC KEY 2": {
            "name": "bla 2",
          }
          ...
      }

Cependant, mon JSON comporte des clés dynamiques que je ne connaîtrai pas au moment de l'exécution, et j'aimerais utiliser Circe pour coder/décoder. Est-ce que j'utilise la bonne façon de représenter ceci en utilisant 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)

6voto

littlenag Points 2124

Je pense que vous pouvez simplement utiliser un Map[String, CarDetails] . Votre ADT devient alors :

import io.circe.generic.JsonCodec

@JsonCodec
case class Cars(cars: Map[String, CarDetails])

@JsonCodec
case class CarDetails(name: String)

Le seul point délicat pourrait être de savoir si vous exigez qu'il y ait au moins un objet CarDetails, ou si zéro est acceptable. Circe semble supporter cats.data.NonEmptyMap si cela s'avère nécessaire.

2voto

Travis Brown Points 56342

La manière la plus simple de traiter un cas comme celui-ci serait probablement de modifier l'option cars membre de la Cars pour que la classe de cas ait un type comme Map[String, CarDetails] en laissant tomber le Car classe de cas tout à fait. Si vous faites cela, votre code fonctionnera exactement tel qu'il est (sans la classe Car ), et décodera l'exemple JSON que vous avez fourni.

Si vous voulez quelque chose de plus proche de la structure de votre classe de cas, vous pouvez faire ce qui suit :

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)

Cela décodera le même JSON, mais inclura les clés dynamiques dans le fichier Car niveau.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X