eike / json-decode-complete / DecodeComplete

This module provides a way to decode JSON objects while making sure that all fields are handled. The interface works similar to json-decode-pipeline. For example,

import Json.Decode as D exposing (Decoder)
import DecodeComplete exposing (..)

type alias User =
    { name : String
    , age : Int
    }

userDecoder : Decoder User
userDecoder =
    object User
        |> required "name" D.string
        |> required "age" D.int
        |> discard "email"
        |> complete

decodes JSON objects that have precisely the fields name, age, and email and turns it into a User record, discarding the email address.

The general usage is as follows: Start decoding the object with object f, where f is the function being called with the results. Then decode the individual fields with require, discard, optional, discardOptional. At the end, turn turn the ObjectDecoder into a normal Decoder by calling complete (or discardRest or rest or restValues).

Starting to decode


type ObjectDecoder a

A decoder for JSON objects that makes sure that all fields in the JSON are handled

object : a -> ObjectDecoder a

Start decoding a JSON object.

Decoding fields

required : String -> Json.Decode.Decoder a -> ObjectDecoder (a -> b) -> ObjectDecoder b

Decode the field given by the String parameter using the given (regular) Decoder. If the field is missing, the decoder fails.

optional : String -> Json.Decode.Decoder a -> a -> ObjectDecoder (a -> b) -> ObjectDecoder b

Decode the field given by the String parameter using the given (regular) Decoder. If the field is missing or the decoder fails, use the provided default value instead.

discard : String -> ObjectDecoder a -> ObjectDecoder a

Require that a field is present, but discard its value.

discardOptional : String -> ObjectDecoder a -> ObjectDecoder a

Discard the value of a field (thus marking it as handled), but simply ignore if its not there.

hardcoded : a -> ObjectDecoder (a -> b) -> ObjectDecoder b

Don’t look at the JSON, simply use the given value.

Finish decoding

complete : ObjectDecoder a -> Json.Decode.Decoder a

Close the ObjectDecoder, turning it into a regular Decoder. If unhandled fields in the JSON remain, this decoder will fail.

discardRest : ObjectDecoder a -> Json.Decode.Decoder a

Turn the ObjectDecoder into a regular Decoder. Ignore if fields remain unhandled.

This might be useful if you only want the check that all fields are handled to occur during development. You can use complete in development and change it into discardRest without having to change anything else.

rest : Json.Decode.Decoder a -> ObjectDecoder (Dict String a -> b) -> Json.Decode.Decoder b

Decode the remaining fields uniformly with the given Decoder, pass the dictionary of the results and close the ObjectDecoder turning it into a regular Decoder.

restValues : ObjectDecoder (Dict String Json.Decode.Value -> b) -> Json.Decode.Decoder b

Finish up the ObjectDecoder, turning it into a regular decoder. Pass a dictionary of the unhandled fields (as Decode.Value values).

Special needs – decoding custom types and versioned data

andThen : (a -> ObjectDecoder b) -> ObjectDecoder a -> ObjectDecoder b

Decide how to proceed based on earlier fields. This can be useful if the JSON represents a sum type or has different versions. For example

userDecoder : Decoder ( String, Int )
userDecoder =
    (object identity
        |> required "version" D.int
    )
        |> andThen
            (\version ->
                case version of
                    0 ->
                        object Tuple.pair
                            |> required "name" D.string
                            |> required "age" D.int

                    1 ->
                        object Tuple.pair
                            |> required "fullName" D.string
                            |> required "age" D.int
                            |> discard "email"

                    _ ->
                        fail "unsupported version"
            )
        |> complete

first decodes the version field. If it is 0, the JSON needs to have (exactly) the fields name and age. If the version is 1, the JSON needs the fields fullName, age and email instead. If the version is anything else, fail.

fail : String -> ObjectDecoder a

An ObjectDecoder that always fails. Can be useful in combination with andThen.