3 votes

Comment décomposer une valeur JSON en un type d'union balisé ?

J'ai des données JSON provenant de Firebase qui ressemblent à ça.

{
  "type": "added",
  "doc": {
    "id": "asda98j1234jknkj3n",
    "data": {
      "title": "Foo",
      "subtitle": "Baz"
    }
  }
}

Le type peut être l'un des suivants "added" , "modified" o "removed" . Doc contient un id et un data champ. Le site data Le champ peut avoir n'importe quelle forme et je suis capable de le décoder correctement.

Je veux utiliser des types d'union pour représenter ces valeurs comme suit,

type alias Doc data =
    (String, data)

type DocChange doc
    = Added doc
    | Modified doc
    | Removed doc

Ici, le Doc représente la valeur contenue dans l'alias de type doc dans le JSON ci-dessus. DocChange représente l'ensemble. Si le type est disons "added" alors le JSON doit être décodé en Added doc et ainsi de suite. Je ne comprends pas comment décoder les types d'union.

Je pense que le andThen de la fonction Json.Decode ressemble à ce dont j'ai besoin, mais je ne parviens pas à l'utiliser correctement.

4voto

glennsl Points 9939

Tout d'abord, il semble que vous vouliez contraindre le doc paramètre de DocChange à un Doc Vous devriez donc le définir comme suit :

type DocChange data
    = Added (Doc data)
    | Modified (Doc data)
    | Removed (Doc data)

Sinon, vous devrez spécifier à plusieurs reprises DocChange (Doc data) dans les annotations de type de vos fonctions, ce qui devient rapidement ennuyeux, et pire encore à mesure que vous l'imbriquez. En tout cas, j'ai continué à utiliser les types tels que vous les avez définis :

decodeDocData : Decoder DocData
decodeDocData =
    map2 DocData
        (field "title" string)
        (field "subtitle" string)

decodeDoc : Decoder data -> Decoder (Doc data)
decodeDoc dataDecoder =
    map2 Tuple.pair
        (field "id" string)
        (field "data" dataDecoder)

decodeDocChange : Decoder data -> Decoder (DocChange (Doc data))
decodeDocChange dataDecoder =
    field "type" string
        |> andThen
            (\typ ->
                case typ of
                    "added" ->
                        map Added
                            (field "doc" (decodeDoc dataDecoder))

                    "modified" ->
                        map Modified
                            (field "doc" (decodeDoc dataDecoder))

                    "removed" ->
                        map Removed
                            (field "doc" (decodeDoc dataDecoder))

                    _ ->
                        fail ("Unknown DocChange type: " ++ typ)
            )

L'astuce ici est de décoder "type" d'abord, puis utiliser andThen pour l'allumer et choisir le décodeur approprié. Dans ce cas, la forme est identique d'un "type" à l'autre, mais il se peut qu'elle ne le soit pas et ce modèle offre la flexibilité nécessaire pour gérer également des formes divergentes. Il pourrait être simplifié en sélectionnant simplement le constructeur et en gardant le reste du décodage commun si vous êtes absolument sûr qu'ils ne divergeront pas.

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