我尝试在榆木中解码一些json。
我正在接收的对象可能具有两种不同的形状。
第一种情况:

{
    ...
    "ChipId": "NotSet"
    ...
}


第二种情况:

{
    ...
    "ChipId": {
      "Item": "0000000000"
    },
    ...
}


因此,第一个可以很容易地用field "ChipId" string解码,但是如果它是复杂的对象,它将失败。
我已经用Decode.andThen尝试过了,但是无法解决。

谢谢您的帮助!

更新1-解码器失败

我尝试的方法是使用Maybe

chipIdDecoder : Decoder String
chipIdDecoder =
    let
        chipIdIdDecoder : Decoder String
        chipIdIdDecoder =
            field "ChipId" (field "Fields" (firstElementDecoder string))

        chooseChipId : Maybe String -> Decoder String
        chooseChipId c =
            case c of
                Just id ->
                    case id of
                        "ChipId" ->
                            chipIdIdDecoder

                        _ ->
                            succeed ""

                Nothing ->
                    fail "Chip Id is invalid"
    in
    nullable (field "ChipId" string)
        |> andThen chooseChipId


我想这里的问题是Maybe期望什么或null而不是期望的东西。 ^^

最佳答案

tl; dr:使用oneOf

在Elm中编写json解码器的一种好方法是从最小的部分开始,编写独立解码每个部分的解码器,然后再升级到下一个级别,并通过将已经存在的较小部分组合在一起来为此编写解码器制作。

例如,在这里,我将从编写解码器开始以分别处理"ChipId"的两种可能形式。第一个只是一个字符串,它当然是随elm/json一起提供的,因此很容易。另一个是具有单个字段的对象,我们将其解码为简单的String

chipIdObjectDecoder : Decoder String
chipIdObjectDecoder =
    field "Item" string


然后,我们需要将它们放在一起,这似乎是您最苦苦挣扎的部分。这里的oneOf函数可以帮助我们解决,其描述为:


尝试一堆不同的解码器。如果JSON可能有几种不同的格式,这将很有用。


听起来正是我们所需要的!要尝试使用string解码器和我们的chipIdObjectDecoder,我们可以编写:

eitherStringOrChipIdObject : Decoder String
eitherStringOrChipIdObject =
    oneOf [ string, chipIdObjectDecoder ]


最后,我们需要解码"ChipId"字段本身:

field "ChipId" eitherStringOrChipIdObject


所有这些都放在一个函数中:

chipIdDecoder : Decoder String
chipIdDecoder =
    let
        chipIdObjectDecoder : Decoder String
        chipIdObjectDecoder =
            field "Item" string

        eitherStringOrChipIdObject : Decoder String
        eitherStringOrChipIdObject =
            oneOf [ string, chipIdObjectDecoder ]
    in
    field "ChipId" eitherStringOrChipIdObject


或者稍微简化一下,因为上述内容非常冗长:

chipIdDecoder : Decoder String
chipIdDecoder =
    let
        chipIdObjectDecoder =
            field "Item" string
    in
    field "ChipId" (oneOf [ string, chipIdObjectDecoder ])


最后一点,由于不清楚您的代码是否过度简化。如果"ChipId"对象不能简化为简单的字符串,则必须使用可以同时容纳StringChipIdObject的通用类型,并将map解码后的值保存为该通用类型。 eitherStringOrChipIdObject可能是这样的:

type alias ChipIdObject = { ... }

type ChipId
    = ChipIdString String
    | ChipIdObject ChipIdObject

eitherStringOrChipIdObject : Decoder ChipId
eitherStringOrChipIdObject =
    oneOf
        [ string |> map ChipIdString
        , chipIdObjectDecoder |> map ChipIdObject
        ]

09-12 18:36
查看更多