JSON to Elm: types and decoders
Elm never trusts incoming data: every JSON value must pass through a decoder that describes its exact shape, which is why Elm apps do not crash on unexpected API responses. Writing those decoders is also the most tedious part of Elm development. This JSON to Elm generator writes record type aliases and the matching decoders and encoders from a sample.
Example
{ "id": 42, "title": "Write docs", "done": false,
"owner": { "name": "Ada", "email": null },
"subtasks": [ { "title": "Outline", "estimate": 2 }, { "title": "Draft" } ] }
Type aliases (types only):
type alias Task =
{ id : Int
, title : String
, done : Bool
, owner : Owner
, subtasks : List Subtask
}
type alias Subtask =
{ title : String
, estimate : Maybe Int
}
The optional estimate is a Maybe Int. The full output also contains decoders written in the pipeline style, one per type, for example:
subtask : Jdec.Decoder Subtask
subtask =
Jdec.succeed Subtask
|> Jpipe.required "title" Jdec.string
|> Jpipe.optional "estimate" (Jdec.nullable Jdec.int) Nothing
Setting up the module
- Install the pipeline package:
elm install NoRedInk/elm-json-decode-pipeline. - Save the output as
QuickType.elm, or rename themodule QuickType exposing (...)line to match your file. - Decode with
Json.Decode.decodeString task jsonString, which returns aResultyou handle with acaseexpression.
The module also exports encoders built with Json.Encode, useful when sending the same data back to a server.
Custom types for mixed values
When a value can have different JSON types, the generator creates an Elm custom type instead of giving up. An array like [1, "a"] produces type X = IntegerInX Int | StringInX String, with a decoder that tries each variant using Jdec.oneOf. Pattern matching with case then forces you to handle every variant, which is exactly Elm's promise of no runtime exceptions. Field names that clash with Elm keywords or generated names are adjusted, so read the type alias carefully after generating.
Adjustments
- Fields that are
nullin the sample become the unit type(); change them toMaybe Stringwith a nullable decoder once you know the real type. - Dates remain
String; decode them with a package such asrtfeldman/elm-iso8601-date-strings. - Record field order in the type alias must match the order of the pipeline steps, since
Jdec.succeed Taskuses the record constructor. Keep them aligned when editing.
Check your sample with the JSON Validator, and explore large responses with the JSON Viewer before generating. Everything runs in your browser.