榆树解码非均匀阵列

时间:2017-07-18 22:23:14

标签: arrays json decode elm

我需要解码头部项目类型为User且所有尾部项目为Nickname的JSON数组。数组长度事先是未知的,我无法更改JSON表示。

JSON示例:

{ "userdata" : [
    {
        "id" : 1,
        "name" : "MyName",
        "email" : "MyName@dot.com"
    },
    {
        "name" : "n1"
    },
    {
        "name" : "n2"
    }
]
} 

我的类型定义:

module Decoders exposing (..)
type alias User =
    { id : Int
    , name : String
    , email : String
    }

type alias Nickname =
    { name : String
    }

type alias Model =
    { user : User
    , nicknames : List Nickname
    }

UserNickname中还有许多其他不同的字段,但我在此处将其缩短以保持示例简单。

解码器:

decodeUser : Json.Decode.Decoder User
decodeUser =
    Json.Decode.Pipeline.decode User
        |> Json.Decode.Pipeline.required "id" (Json.Decode.int)
        |> Json.Decode.Pipeline.required "name" (Json.Decode.string)
        |> Json.Decode.Pipeline.required "email" (Json.Decode.string)


decodeNickname : Json.Decode.Decoder Nickname
decodeNickname =
    Json.Decode.Pipeline.decode Nickname
        |> Json.Decode.Pipeline.required "name" (Json.Decode.string)


decodeModel : Json.Decode.Decoder Model
decodeModel =
    Json.Decode.Pipeline.decode Model
        |> Json.Decode.Pipeline.required "userdata" (Json.Decode.index 0 decodeUser)
        |> Json.Decode.Pipeline.hardcoded [ Nickname "Nick", Nickname "Names" ]

测试:

decodesModel : Test
decodesModel =
    test "Decodes a user and list of nicknames" <|
        \() ->
            let
                input =
                    """
                      { "userdata" : [
                        {
                          "id" : 1,
                          "name" : "MyName",
                          "email" : "MyName@dot.com"
                        },
                        {
                          "name" : "n1"
                        },
                        {
                          "name" : "n2"
                        }
                      ]
                      }
                    """

                decodedOutput =
                    Json.Decode.decodeString
                        Decoders.decodeModel
                        input

                nicknames =
                    [ Decoders.Nickname "n1", Decoders.Nickname "n2" ]

                user =
                    Decoders.User 1 "MyName" "MyName@dot.com"

                expectation =
                    Decoders.Model user nicknames
            in
                Expect.equal decodedOutput
                    (Ok expectation)

由于我刚刚对Nickname反序列化进行了硬编码,测试失败了:

✗ Decodes a user and list of nicknames

Ok { user = { id = 1, name = "MyName", email = "MyName@dot.com" }, nicknames = [{ name = "n1" },{ name = "n2" }] }
╷
│ Expect.equal
╵
Ok { user = { id = 1, name = "MyName", email = "MyName@dot.com" }, nicknames = [{ name = "Nick" },{ name = "Names" }] }

删除头项目并将数组其余部分反序列化为Nickname列表的最佳方法是什么?

1 个答案:

答案 0 :(得分:1)

我会通过首先制作一个高阶解码器来接近这一点,该解码器将两个解码器作为输入:第一个解码器解码列表的头部,第二个解码器解码尾部。签名可以是这样的:

headAndTailDecoder : Decoder a -> Decoder b -> Decoder ( a, List b )

以下是我最初尝试实现此功能。它有点冗长,因为它首先将列表解码为Json.Decode.Value项列表,然后在结果列表中运行解码器:

import Json.Decode as JD exposing (Decoder)
import Result.Extra exposing (combine)

headAndTailDecoder : Decoder a -> Decoder b -> Decoder ( a, List b )
headAndTailDecoder head tail =
    JD.list JD.value
        |> JD.andThen
            (\values ->
                case values of
                    [] ->
                        JD.fail "Empty list"

                    h :: t ->
                        case ( JD.decodeValue head h, List.map (JD.decodeValue tail) t |> combine ) of
                            ( Ok headDecoded, Ok tailDecoded ) ->
                                JD.succeed (headDecoded, tailDecoded)

                            _ ->
                                JD.fail "Invalid"
            )

这可能会被优化,但它可以完成工作。针对您的输入运行产生:

JD.field "userdata" (headAndTailDecoder decodeUser decodeNickname)
    |> JD.map (\(h, t) -> Model h t)

-- yields: Ok { user = { id = 1, name = "MyName", email = "MyName@dot.com" }, nicknames = [{ name = "n1" },{ name = "n2" }] }