如何将JSON值解压缩为标记的联合类型?

时间:2018-11-05 06:01:05

标签: json decode elm union-types

我有一些来自Firebase的JSON

{
  "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类型别名表示上面JSON中doc字段中包含的值。 DocChange代表整个事情。如果类型为"added",则JSON必须解码为Added doc,依此类推。我不明白如何解码联合类型。

我认为andThen中的Json.Decode函数看起来像我需要的,但是我无法正确使用它。

1 个答案:

答案 0 :(得分:4)

首先,您似乎想将doc的{​​{1}}参数限制为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"打开它并选择适当的解码器。在这种情况下,“类型”上的形状是相同的,但可能并非完全相同,并且该图案也提供了处理各种形状的灵活性。如果您完全确定它们不会发生分歧,可以简化为只选择构造函数并使其余解码保持公共状态。