Skip to content

Instantly share code, notes, and snippets.

@focusaurus
Created December 5, 2016 00:26
Show Gist options
  • Select an option

  • Save focusaurus/1085181366a6399414f0b0049ece3750 to your computer and use it in GitHub Desktop.

Select an option

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

1602 commented Jul 18, 2017

Copy link
Copy Markdown

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
Copy Markdown

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

@raoul2000

Copy link
Copy Markdown

👍

@nahiyan

nahiyan commented Jan 19, 2019

Copy link
Copy Markdown

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

raphaelpereira commented Jan 23, 2019

Copy link
Copy Markdown

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

@nqthqn

nqthqn commented Mar 3, 2019

Copy link
Copy Markdown

Maybe call it unknownTheme

@LimmaPaulus

LimmaPaulus commented May 24, 2019

Copy link
Copy Markdown

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
Copy Markdown

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

type Theme
= Light
| Dark Int

@asonix

asonix commented Jul 10, 2019

Copy link
Copy Markdown

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
Copy Markdown

Great example!!

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