简体   繁体   中英

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. I could not find a decode function in NoRedInk/elm-json-decode-pipeline and I do not believe the := infix operator is still valid.

The second addresses the slightly different question of conditionally decoding based on the value of a JSON field.

If I have data coming over a port and the following types:

import Json.Decode exposing (Decoder, map, oneOf, string, succeed, 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?

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.

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 .

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 .

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.

-- 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
        ]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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