简体   繁体   English

如何在 elm 0.19 中将 Json.Decode.Value 解码为标记联合类型?

[英]How to a decode Json.Decode.Value into tagged union type in elm 0.19?

I see我懂了

I don't believe the first is relevant for elm 0.19.我不相信第一个与 elm 0.19 相关。 I could not find a decode function in NoRedInk/elm-json-decode-pipeline and I do not believe the := infix operator is still valid.我在NoRedInk/elm-json-decode-pipeline 中找不到decode函数,我不相信:=缀运算符仍然有效。

The second addresses the slightly different question of conditionally decoding based on the value of a JSON field.第二个解决了根据 JSON 字段的值进行条件解码的稍微不同的问题。

If I have data coming over a port and the following types:如果我有数据通过端口和以下类型:

import Json.Decode exposing (Decoder, map, oneOf, string, succeed, andThen, map2, map4)导入 Json.Decode 暴露 (Decoder, map, oneOf, string, success, andThen, map2, map4)

import Json.Decode exposing (Decoder, andThen, map, map2, map4, oneOf, string, succeed, field)


port loginResponse : (Value -> msg) -> Sub msg


type Status
    = Authenticated Data
    | Unauthenticated (Maybe Error)


type alias Data =
    { accessToken : String
    , email : String
    , name : String
    , nickname : String
    }


dataDecoder : Decoder Data
dataDecoder =
    map4 Data
        (field "accessToken" string)
        (field "email" string)
        (field "name" string)
        (field "nickname" string)


type alias Error =
    { error : String
    , errorDescription : String
    }


errorDecoder : Decoder Error
errorDecoder =
    map2 Error
        (field "error" string)
        (field "errorDescription" string)

How can I write a decoder for tagged union type Status to decode the data coming back in from the port?如何为标记联合类型Status编写解码器以解码从端口返回的数据?

Best I got so far is something like到目前为止我得到的最好的是

statusDecoder : Decoder Status
statusDecoder =
    oneOf
        [ dataDecoder andThen (\data -> succeed (Authenticated data)
        , errorDecoder andThen (\error -> succeed (Unauthenticated (Just error)))
        ]

which isn't valid, or这是无效的,或

getStatus : Json.Decode.Value -> Status
getStatus value =
    let
        decodedData =
            decodeValue dataDecoder value
    in
    case decodedData of
        Ok data ->
            Authenticated data

        Err _ ->
            let
                decodedError =
                    decodeValue errorDecoder value
            in
            case decodedError of
                Ok error ->
                    Unauthenticated (Just error)

                Err err ->
                    Unauthenticated (Just { error = "There was a problem decoding the JSON", errorDescription = "" })

which is really ugly and doesn't feel right.这真的很丑,感觉不对。

you've done all the hard work.你已经完成了所有的艰苦工作。 This should be all you need now.这应该就是你现在所需要的。 Note .map is the quick way to do your case statement stuff.注意.map是执行 case 语句的快捷方式。

import Json.Decode as Decode exposing (Decoder)
statusDecoder =
    Decode.oneOf
        [ Decode.map Authenticated dataDecoder
        , Decode.map Unauthenticated <| Decode.maybe errorDecoder
        ]

The solution I went with is to use Json.Decode.map to translate the Decoder Data and Decoder Error types to Decoder Status types using the Status type constructors Authenticated data and Unauthenticated Maybe error .我采用的解决方案是使用Json.Decode.map使用Status类型构造函数Authenticated dataUnauthenticated Maybe errorDecoder DataDecoder Error类型转换为Decoder Status类型。

I can then use the Json.Decode.oneOf function to decode into one or the other depending on the shape of the data because both decoders are now of the same type Decoder Status .然后我可以使用Json.Decode.oneOf函数根据数据的形状解码成一个或另一个,因为两个解码器现在都是相同类型的Decoder Status

dataDecoder : Decoder Status
dataDecoder =
    map Authenticated <|
        map4 Data
            (field "accessToken" string)
            (field "email" string)
            (field "name" string)
            (field "nickname" string)


errorDecoder : Decoder Status
errorDecoder =
    map Unauthenticated <|
        maybe <|
            map2 Error
                (field "error" string)
                (field "errorDescription" string)


statusDecoder : Decoder Status
statusDecoder =
    oneOf [ dataDecoder, errorDecoder ]

For brevity you can then use the Json.Decode.decodeValue function with the statusDecoder function defined above to decode the Json.Decode.Value coming out of the port in your update function.为简洁起见,您可以使用Json.Decode.decodeValue函数和上面定义的statusDecoder函数来解码从更新函数中的端口出来的Json.Decode.Value

-- UPDATE

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        ReceiveLoginResponse response ->
            let
                decodedResponse = decodeValue Auth.statusDecoder response
            in
            case decodedResponse of
                Ok status ->
                    ( { model
                        | authStatus = status 
                      }
                      , Cmd.none
                    )
         ...

-- SUBSCRIPTIONS


subscriptions : Model -> Sub Msg
subscriptions model =
    Sub.batch
        [ loginResponse ReceiveLoginResponse
        ]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM