ztsu
ztsu

Reputation: 25

How to decode JSON to record built by a data constructor?

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?

Upvotes: 1

Views: 369

Answers (2)

Chad Gilbert
Chad Gilbert

Reputation: 36385

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 ]

Upvotes: 3

Chad Gilbert
Chad Gilbert

Reputation: 36385

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": "[email protected]"}}]
  """

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

-- Ouputs:
-- Ok ([Anonymous,Named { name = "John Doe", email = "[email protected]" }])

Upvotes: 3

Related Questions