Elm - 解码递归多路树

Elm - decoding a recursive multiway tree

我正在研究这种类型的递归树

type Node anyType
  = Leaf Id (Maybe anyType) Name
  | Tree Id (List (Node anyType)) Name

哪里

type Id
  = Id Int
  | Root

我正在尝试将这种 json 解码成

{
  "id": "root",
  "entries": [
    {
      "id": 1,
      "value": 0,
      "name": "New Entry"
    },
    {
      "id": 2,
      "entries": [
        {
          "id": 4,
          "value": 0,
          "name": "New Entry"
        }
      ],
      "name": "New Entry"
    }
  ],
  "name": "Budget"
}

为了解码我正在使用这些解码器的 Id 类型

rootDecoder =
(Decode.field "id" Decode.string)
    |> Decode.andThen
        (\str ->
            if str == "root" then
                (Decode.succeed Root)

            else
                Decode.fail <| "[exactMatch] tgt: " ++ "root" ++ " /= " ++ str
        )


intIdDecoder =
    Decode.map Id (Decode.field "id" Decode.int)


idDecoder =
    Decode.oneOf
        [ rootDecoder
        , intIdDecoder
        ]

为了解码树结构,我尝试了以下方法,使用 Json.Decode.Pipeline:

leafDecoder valueDecoder =
    Decode.succeed Leaf
        |> required "id" idDecoder
        |> required "value" valueDecoder
        |> required "name" Decode.string


treeDecoder valueDecoder =
    Decode.succeed Tree
        |> required "id" idDecoder
        |> required "entries"
            (Decode.list
                (Decode.lazy
                    (\_ ->
                        Decode.oneOf
                            [ leafDecoder valueDecoder
                            , treeDecoder valueDecoder
                            ]
                    )
                )
            )
        |> required "name" Decode.string

但是当我尝试解码结构时出现以下错误:

The Json.Decode.oneOf at json.budget.entries[0] failed in the following 2 ways: (1) The Json.Decode.oneOf at json.id failed in the following 2 ways: (1) Problem with the given value: 1 Expecting an OBJECT with a field named `id` (2) Problem with the given value: 1 Expecting an OBJECT with a field named `id` (2) Problem with the given value: { "id": 1, "value": 0, "name": "New Entry" } Expecting an OBJECT with a field named `entries`

但我不明白为什么 identries 字段都在那里,但它会抱怨。

我做错了什么?

在此先感谢您的帮助

问题是 rootDecoderintIdDecoder 都被定义为通过 Decode.field "id" ... 在对象中查找名为 "id" 的字段。在 treeDecoder 中,您首先获取 "id" 字段,因此您的解码器对某些 JSON 有效,例如

// Not what you're looking for
{
  "id": {
    "id": ...
  },
  ...
}

您可以通过删除这些解码器中的 Decode.field "id" 部分来解决此问题:

rootDecoder = Decode.string
    |> Decode.andThen
        (\str ->
            if str == "root" then
                (Decode.succeed Root)

            else
                Decode.fail <| "[exactMatch] tgt: " ++ "root" ++ " /= " ++ str
        )


intIdDecoder =
    Decode.map Id Decode.int

在您的 leafDecodertreeDecoder 中,您有以下几行:

leafDecoder valueDecoder =
    Decode.succeed Leaf
        |> required "id" idDecoder
        -- rest of function omitted...

treeDecoder valueDecoder =
    Decode.succeed Tree
        |> required "id" idDecoder
        -- rest of function omitted...

这些都将提取当前对象中字段 id 的值,并将该值传递给 idDecoder,它用 rootDecoder 调用 Decode.oneOfintIdDecoder.

但是,在您的 rootDecoderintIdDecoder 中,您有以下内容:

rootDecoder =
    (Decode.field "id" Decode.string)
        |> Decode.andThen
            -- rest of function omitted...

intIdDecoder =
    Decode.map Id (Decode.field "id" Decode.int)

这些解码器试图从当前对象中提取名为 id 的字段的值。但是您将 id 属性 的值传递给这些函数,而不是包含此 属性.

的对象

如果您的 id 嵌套在仅包含 id 属性的对象中,这些解码器将起作用,例如:

{
  "id": {"id": "root"},
  "entries": [
    {
      "id": {"id": 1},
      "value": 0,
      "name": "New Entry"
    },
    ...

解决方法是删除 rootDecoderintIdDecoder 中对 Decode.field 的调用,因为这些解码器已经传递了 id 字段的值:

rootDecoder =
    Decode.string
        |> Decode.andThen
            -- rest of function as before, and omitted for brevity...


intIdDecoder =
    Decode.map Id Decode.int