FormatterHQ

JSON to Haskell (Aeson) Generator

Generate Haskell record types from JSON with FromJSON and ToJSON instances for the Aeson library. Optional fields become Maybe values.

Input JSON
Output HASKELL
Ready.

How to use the JSON to Haskell

  1. Paste your JSON sample (an object, or an array of objects) into the Input editor, or use Sample, Upload file or Load URL, or press Sample to try one.
  2. Set the Top-level class name, for example Task; it is converted to PascalCase. Nested types are named after their property keys.
  3. Generate with Types only (no helpers) off to include the Aeson FromJSON / ToJSON instances and a decodeTopLevel function.
  4. Press Generate (it also re-runs automatically as you edit), then use Copy or Download.

JSON to Haskell with Aeson

Aeson is the standard JSON library for Haskell. It decodes into your own data types through FromJSON instances and encodes through ToJSON. This JSON to Haskell generator writes both the record types and the instances from a sample, together with a decodeTopLevel function, giving you a module that compiles with Aeson and a couple of common packages.

Example

{ "id": 42, "title": "Write docs", "done": false,
  "owner": { "name": "Ada", "email": null },
  "subtasks": [ { "title": "Outline", "estimate": 2 }, { "title": "Draft" } ] }
data Task = Task
    { taskIDTask :: Int
    , titleTask :: Text
    , doneTask :: Bool
    , ownerTask :: Owner
    , subtasksTask :: [Subtask]
    } deriving (Show)

data Subtask = Subtask
    { titleSubtask :: Text
    , estimateSubtask :: Maybe Int
    } deriving (Show)

instance FromJSON Subtask where
    parseJSON (Object v) = Subtask
        <$> v .: "title"
        <*> v .:? "estimate"

Required keys are read with .: and optional keys with .:?, which yields Nothing when the key is absent. Strings use Text rather than String, as is idiomatic for Aeson.

Sum types for mixed values

When a JSON value can have several types, such as an array containing both numbers and strings, the generator creates a Haskell sum type, for example data X = IntegerInX Int | StringInX Text, with FromJSON and ToJSON instances that try each alternative. Numbers without decimals become Int and decimals Float; change Float to Double if you need more precision, since Aeson supports both.

Why the field names are so long

Haskell record fields are functions in the module's namespace, so two records cannot both have a field called title. The generator avoids collisions by appending the type name: titleTask, titleSubtask. With GHC's DuplicateRecordFields and OverloadedRecordDot extensions you can shorten them to title and write task.title; the FromJSON instances keep the JSON keys explicit, so decoding is unaffected.

Using the module

import qualified Data.ByteString.Lazy as BL

main = do
    bytes <- BL.readFile "task.json"
    case decodeTopLevel bytes of
        Just task -> print (titleTask task)
        Nothing   -> putStrLn "Invalid JSON"

The module enables OverloadedStrings and StrictData and imports Data.Aeson, Data.Text and Data.HashMap.Strict, so add aeson, text, bytestring and unordered-containers to your package dependencies. For better error messages, switch decode to eitherDecode, which returns the reason decoding failed.

Validate your sample first with the JSON Validator. For a schema-based contract shared with other languages, try JSON to JSON Schema. Generation runs in your browser.

Frequently asked questions

Which packages does the generated Haskell code need?
aeson, text, bytestring and unordered-containers. The module also enables the OverloadedStrings and StrictData language extensions.
Why are record fields suffixed with the type name?
Record fields share one namespace in Haskell, so identical names in different records would clash. The suffix keeps them unique.
How are optional JSON fields handled?
They are Maybe fields decoded with .:? so a missing key becomes Nothing instead of a decoding failure.
How do I see why decoding failed?
Replace decode with eitherDecode, which returns Left with an error message describing the mismatch.

Last updated October 3, 2026

Related tools

XML to Haskell Generate Haskell records with Aeson instances from a XML sample instantly, with nested types detected automatically. JSON to Java Generate Java POJO classes with getters and setters from a JSON sample instantly, with nested types detected automatically. JSON to JavaScript PropTypes Generate JavaScript PropTypes React PropTypes definitions from a JSON sample instantly, with nested types detected automatically. JSON to JSON Schema Generate JSON Schema a JSON Schema document from a JSON sample instantly, with nested types detected automatically. JSON to Kotlin Generate Kotlin data classes from a JSON sample instantly, with nested types detected automatically. JSON to Laravel Model & Migration Generate a Laravel Eloquent model ($fillable, casts, relations) and a matching migration from a sample JSON record. JSON to Mongoose Schema Generate a Mongoose schema and model from a sample JSON document, detecting Date, ObjectId, arrays and nested objects. JSON to Objective-C Generate Objective-C classes with properties from a JSON sample instantly, with nested types detected automatically. JSON to PHP Generate PHP classes with typed properties from a JSON sample instantly, with nested types detected automatically. JSON to Pike Generate Pike classes from a JSON sample instantly, with nested types detected automatically. JSON to Python Generate Python dataclasses with type hints from a JSON sample instantly, with nested types detected automatically. JSON to Ruby Generate Ruby Dry::Struct classes from a JSON sample instantly, with nested types detected automatically.