Search code examples
elm

Ignore invalid item when decoding list


is it possible to ignore invalid items when decoding the list? example: I have a Model

type Type
    = A
    | B

type alias Section =
    { sectionType : Type
    , index : Int
    }


getTypeFromString : String -> Maybe Type
getTypeFromString input =
    case input of
        “a” ->
            Just A

        “b” ->
            Just B

        _ ->
            Nothing

decodeType : Decoder Type
decodeType =
    Decode.string
        |> Decode.andThen
            (\str ->
                case getTypeFromString str of
                    Just sectionType ->
                        Decode.succeed sectionType

                    Nothing ->
                        Decode.fail <| ("Unknown type" ++ str)
            )


decodeSection : Decoder Section
decodeSection =
    Decode.map2 Section
        (Decode.field "type" decodeType)
        (Decode.field "index" Decode.int)

if I decode the JSON

{
  "sections": [{type: "A", index: 1}, {type: "invalid-type", index: 2}]
}

I expect my sections = [ {type = A, index= 1} ]


Solution

  • Given the comment by Quan Vo about one of many fields could be invalid, using Decode.oneOf might be a better fit.

    You write the decoders for each field. If any field is illegal, the Section decoder fails and in oneOf, Nothing is returned.

    (Here I am also using Json.Decode.Pipeline from NoRedInk).

    import Json.Decode as Decode exposing (Decoder)
    import Json.Decode.Pipeline exposing (required)
    
    
    type Type
        = A
        | B
    
    
    type alias Section =
        { sectionType : Type
        , index : Int
        }
    
    
    getTypeFromString : String -> Maybe Type
    getTypeFromString input =
        case input |> String.toLower of
            "a" ->
                Just A
    
            "b" ->
                Just B
    
            _ ->
                Nothing
    
    
    decodeType : Decoder Type
    decodeType =
        Decode.string
            |> Decode.andThen
                (\str ->
                    case getTypeFromString str of
                        Just sectionType ->
                            Decode.succeed sectionType
    
                        Nothing ->
                            Decode.fail <| ("Unknown type" ++ str)
                )
    
    
    decodeSection : Decoder Section
    decodeSection =
        Decode.succeed Section
            |> required "type" decodeType
            |> required "index" Decode.int
    
    -- Either we succeed in decoding a Section or fail on some field.
    decodeMaybeSection : Decoder (Maybe Section)
    decodeMaybeSection =
        Decode.oneOf
            [ decodeSection |> Decode.map Just
            , Decode.succeed Nothing
            ]
    
    
    decodeSections : Decoder (List Section)
    decodeSections =
        Decode.list decodeMaybeSection
            |> Decode.map (List.filterMap identity)