Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ignore invalid item when decoding list

Tags:

elm

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

like image 878
Quan Vo Avatar asked Feb 26 '26 19:02

Quan Vo


2 Answers

Generally the way you can deal with these is by decoding it to an Elm type that expresses the options, and then post processing with a map.

So for instance in your example, I would go for something like this:

decodeMaybeType : Decoder (Maybe Type)
decodeMaybeType =
    Decode.string
        |> Decode.map getTypeFromString 


decodeMaybeSection : Decoder (Maybe Section)
decodeMaybeSection =
    Decode.map2 (\maybeType index -> Maybe.map (\t -> Section t index) maybeType)
        (Decode.field "type" decodeMaybeType)
        (Decode.field "index" Decode.int)


decodeSections : Decoder (List Section)
decodeSections =
    Decode.list decodeMaybeSection
       |> Decode.map (List.filterMap identity)

NB: List.filterMap identity is a List (Maybe a) -> List a, it filters out the Nothing and gets rid of the Maybes in one go.

like image 171
Jakub Hampl Avatar answered Feb 28 '26 13:02

Jakub Hampl


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)
like image 33
PerLundholm Avatar answered Feb 28 '26 14:02

PerLundholm



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!