Search code examples
jsonfunctional-programmingelmdecoder

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.


Solution

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