Как декодировать Json.Decode.Value в тегированный тип объединения в вязе 0.19?

Я понимаю

Я не верю, что первое актуально для elm 0.19. Мне не удалось найти функцию decode в NoRedInk /elm-json-decode-pipeline, и я не верю, что инфиксный оператор := все еще действителен.

Второй касается немного другого вопроса условного декодирования на основе значения поля JSON.

Если у меня есть данные, поступающие через порт, и следующие типы:

импортировать Json.Decode, раскрывающий (декодер, карта, oneOf, строка, успех и затем, карта2, карта4)

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)

Как я могу написать декодер для помеченного типа объединения Status для декодирования данных, возвращающихся из порта?

Лучшее, что я получил до сих пор, это что-то вроде

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

что недействительно или

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 = "" })

что действительно уродливо и не кажется правильным.


person Nick    schedule 28.07.2019    source источник


Ответы (2)


вы проделали всю тяжелую работу. Это должно быть все, что вам сейчас нужно. Примечание .map - это быстрый способ сделать ваш оператор case.

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

Я решил использовать Json. Decode.map для преобразования типов Decoder Data и Decoder Error в типы Decoder Status с помощью конструкторов типов Status Authenticated data и Unauthenticated Maybe error.

Затем я могу использовать 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 ]

Для краткости вы можете использовать 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
        ]
person Nick    schedule 29.07.2019