Как распаковать значение JSON в тегированный тип объединения? - PullRequest
0 голосов
/ 05 ноября 2018

У меня есть какой-то JSON от firebase, который выглядит так

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

Тип может быть одним из "added", "modified" или "removed". Doc содержит поля id и data. Поле data может иметь любую форму, и я могу правильно его декодировать.

Я хочу использовать типы объединений для представления этих значений следующим образом:

type alias Doc data =
    (String, data)

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

Здесь псевдоним типа Doc представляет значение, содержащееся в поле doc в JSON выше. DocChange представляет все это. Если тип имеет значение "added", то JSON должен декодироваться в Added doc и так далее. Я не понимаю, как декодировать типы союзов.

Я думаю, что функция andThen из Json.Decode выглядит так, как мне нужно, но я не могу правильно ее использовать.

1 Ответ

0 голосов
/ 05 ноября 2018

Прежде всего, кажется, что вы хотите ограничить параметр doc для DocChange значением Doc, поэтому вам, вероятно, следует определить его следующим образом:

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

В противном случае вам придется многократно указывать DocChange (Doc data) в аннотациях типов функций, что быстро становится раздражающим, и чем хуже, тем больше вы его вкладываете. В любом случае, я продолжил использовать типы, как вы их определили:

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)
            )

Хитрость заключается в том, чтобы сначала декодировать "type", а затем использовать andThen, чтобы включить его и выбрать подходящий декодер. В этом случае форма одинакова для всех «типов», но это может быть не так, и этот шаблон также дает возможность обрабатывать расходящиеся формы. Это можно упростить, просто выбрав конструктор и сохранив остальную часть общего декодирования, если вы абсолютно уверены, что они не расходятся.

...