Skip to content

Instantly share code, notes, and snippets.

@focusaurus
Created December 5, 2016 00:26
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save focusaurus/1085181366a6399414f0b0049ece3750 to your computer and use it in GitHub Desktop.
Save focusaurus/1085181366a6399414f0b0049ece3750 to your computer and use it in GitHub Desktop.
Decode JSON string enum into elm union type
module A exposing (..)
import Json.Decode as JD
import Json.Encode as JE
type alias User =
{ id : Int
, theme : Theme
}
type Theme
= Light
| Dark
userDecoder =
(JD.map2 User
( JD.field "id" JD.int )
( JD.field "theme" decodeTheme )
)
-- Can't figure out how to make a custom Json.Decoder.Decoder a
decodeTheme toDecode =
let
valueDe =
JD.decodeString JD.string toDecode
in
case valueDe of
Ok name ->
if (String.toLower name) == "light" then
Light
else
Dark
Err msg ->
Dark
@1602
Copy link

1602 commented Jul 18, 2017

Replace your decodeTheme function with the following themeDecoder

themeDecoder : Decoder Theme
themeDecoder =
    Decode.string
        |> Decode.andThen (\str ->
           case str of
                "Light" ->
                    Decode.succeed Light
                "Dark" ->
                    Decode.succeed Dark
                somethingElse ->
                    Decode.fail <| "Unknown theme: " ++ somethingElse
        )

@crypticmind
Copy link

@1602 that's a cool example about custom decoders that should be in the docs.

@raoul2000
Copy link

👍

@nahiyan
Copy link

nahiyan commented Jan 19, 2019

When you want to address "something else" in your case expression, just use "_" but please refrain from using keywords like "somethingElse" in your example, it can be misleading to beginners.

@raphaelpereira
Copy link

raphaelpereira commented Jan 23, 2019

@nahiyan @1602 used "somethingElse" to include it on error message.

@nqthqn
Copy link

nqthqn commented Mar 3, 2019

Maybe call it unknownTheme

@LimmaPaulus
Copy link

LimmaPaulus commented May 24, 2019

How to do this if custom type contains values that should also be decoded from JSON? If, for example, Dark contains Int-value telling how dark it is.

@bbuckley
Copy link

Can anyone answer LimmaPaulus' question? How would themeDecoder look if Dark had a payload?

type Theme
= Light
| Dark Int

@asonix
Copy link

asonix commented Jul 10, 2019

I haven't tried this myself, but this is what I imagine would work

So your JSON might look like

{
    "theme": "Light"
}

or

{
    "theme": ["Dark", 2]
}

So your decoder will need to look like this

type Theme
    = Light
    | Dark Int

type Intermediate =
    Intermediate String (Maybe Int)

decoder : Decoder Theme
decoder =
    Decode.map2 Intermediate (index 0 Decode.string) (index 1 Decode.maybe <| Decode.int)
        |> Decode.andThen fromIntermediate

fromIntermediate : Intermediate -> Decoder Theme
fromIntermediate (Intermediate string maybeInt) =
    case (string, maybeInt) of
        ("light", Nothing) ->
            Decode.succeed Light

        ("dark", Just int) ->
            Decode.succeed <| Dark int

        other ->
            Decode.fail "Invalid json"

@ohri-anurag
Copy link

Great example!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment