解码递归多路树

问题描述 投票:0回答:2

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

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`

但我不明白为什么,因为字段

id
entries
都在那里,但它却抱怨。

我做错了什么?

json elm multiway-tree
2个回答
3
投票

在您的

leafDecoder
treeDecoder
中,您有以下几行:

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
,后者使用
Decode.oneOf
rootDecoder
调用
intIdDecoder

但是,在您的

rootDecoder
intIdDecoder
中,您有以下内容:

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"
    },
    ...

修复方法是删除

Decode.field
rootDecoder
中对
intIdDecoder
的调用,因为这些解码器已经传递了
id
字段的值:

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


intIdDecoder =
    Decode.map Id Decode.int

2
投票

问题在于,

rootDecoder
intIdDecoder
都被定义为通过
"id"
在对象中查找名为
Decode.field "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
© www.soinside.com 2019 - 2024. All rights reserved.