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`

但我不明白为什么,因为这两个领域identries在那里,但它却抱怨。

我究竟做错了什么?

在此先感谢您的帮助

4

2 回答 2

3

在你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当前对象中字段的值并将该值传递给,它使用andidDecoder调用。Decode.oneOfrootDecoderintIdDecoder

但是,在你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如果您的 s 嵌套在仅包含属性的对象中,这些解码器将起作用id,例如:

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

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

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


intIdDecoder =
    Decode.map Id Decode.int
于 2022-02-11T21:21:56.327 回答
2

问题是rootDecoderintIdDecoder都被定义为"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
于 2022-02-11T21:20:52.207 回答