榆树解码非均匀数组
elm decode non-uniform array
我需要解码一个 JSON 数组,其中头项是 User
类型,所有尾项都是 Nickname
类型。数组长度事先未知,我无法更改 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
}
User
和 Nickname
中还有许多其他不同的字段,但我已将其缩短以简化示例。
解码器:
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
的列表的最佳方法是什么?
我会通过首先制作一个高阶解码器来解决这个问题,该解码器将两个解码器作为输入:第一个解码器解码列表的头部,第二个解码器解码列表的尾部。签名可能是这样的:
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" }] }
我需要解码一个 JSON 数组,其中头项是 User
类型,所有尾项都是 Nickname
类型。数组长度事先未知,我无法更改 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
}
User
和 Nickname
中还有许多其他不同的字段,但我已将其缩短以简化示例。
解码器:
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
的列表的最佳方法是什么?
我会通过首先制作一个高阶解码器来解决这个问题,该解码器将两个解码器作为输入:第一个解码器解码列表的头部,第二个解码器解码列表的尾部。签名可能是这样的:
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" }] }