1

There is the type

type User
  = Anonymous
  | Named {name : String, email : String}

Json.Decode.object2 doesn't fit here because its first arguments type is (a -> b -> c) but Named has { email : String, name : String } -> User type.

How to decode to User?

Chad Gilbert
  • 36,115
  • 4
  • 89
  • 97
ztsu
  • 25
  • 7

2 Answers2

3

Since your Named constructor takes a record as a parameter, it might be a little cleaner to create a type alias for the name and email record.

type alias NamedUserInfo =
  { name : String
  , email : String
  }

You could then redefine User to use the alias:

type User
  = Anonymous
  | Named NamedUserInfo

While the above isn't strictly necessary, I find that aliasing record types proves useful in many ways down the road. Here it is useful because it gives us a constructor NamedUserInfo that lets us clearly define a decoder:

import Json.Decode exposing (..)

namedUserInfoDecoder : Decoder NamedUserInfo
namedUserInfoDecoder =
  object2
    NamedUserInfo
    ("name" := string)
    ("email" := string)

And finally, your user decoder could be constructed like this:

userDecoder : Decoder User
userDecoder =
  oneOf
    [ null Anonymous
    , object1 Named namedUserInfoDecoder
    ]

You can run your example through a quick test like this:

exampleJson =
  """
    [{"user":null}, {"user": {"name": "John Doe", "email": "j.doe@mailg.com"}}]
  """

main =
  text <| toString <| decodeString (list ("user" := userDecoder)) exampleJson

-- Ouputs:
-- Ok ([Anonymous,Named { name = "John Doe", email = "j.doe@mailg.com" }])
Chad Gilbert
  • 36,115
  • 4
  • 89
  • 97
  • Thank you for complete answer. I'd guessed it is not possible. I'l rethink my models and will try not to use records with data constructors. – ztsu May 30 '16 at 16:57
3

Another way of doing this could be to define a function that accepts a name and email and returns your Named constructor:

userDecoder : Decoder User
userDecoder =
  let
    named =
      object2
        (\n e -> Named { name = n, email = e })
        ("name" := string)
        ("email" := string)
  in
    oneOf [ null Anonymous, named ]
Chad Gilbert
  • 36,115
  • 4
  • 89
  • 97