CalcCafe

JSON to Haskell

Paste JSON and instantly get quicktype-style Haskell record definitions with inferred field types and nested data types.

Example

Input JSON:

{ "id": 1, "name": "Ada", "tags": ["a"], "address": { "city": "NYC" } }

Generated Haskell:

data Root = Root
 { rootId :: Int
 , rootName :: Text
 , rootTags :: [Text]
 , rootAddress :: Address
 } deriving (Show, Eq, Generic)

data Address = Address
 { addressCity :: Text
 } deriving (Show, Eq, Generic)

How it works

The tool parses your JSON, infers a Haskell type for every value (Int, Double, Bool, Text, lists, Maybe for nulls), and emits a record per object with prefixed field names. The top-level type is named Root and nested objects get their own data declarations.

Good to know

JSON to Haskell turns a sample JSON document into idiomatic Haskell record types, so you can model an API response or config file in code without hand-writing every data declaration. It is aimed at Haskell developers working with Aeson who want a fast head start on the types that go alongside FromJSON/ToJSON instances. The whole thing runs in your browser, so you can paste production payloads without them ever leaving your device.

Reach for it when you receive a new endpoint's JSON and need a matching record skeleton, when you are sketching the data layer of a service, or when an upstream schema changed and you want to regenerate types quickly. The output is plain text you can paste straight into a module and refine, rather than a finished, compile-ready file.

To read the result, start with the Root type at the top: it always represents the outermost JSON value, and every nested object becomes its own data declaration named after its key (for example address produces an Address type). Field names are prefixed with the lowercased type name (rootId, addressCity) because Haskell records share one namespace per module, and the generated header includes the DeriveGeneric and OverloadedStrings pragmas plus imports for Text, Value, and Generic.

A practical caveat: inference is based only on the single sample you paste, so it cannot see fields that happen to be absent or null in that example. Watch for a few signals that need manual review:

Frequently asked questions

How are field names generated?
Each record field is prefixed with the lowercased type name (e.g. rootId, addressCity) to avoid Haskell's record-field name clashes, since plain records share a single namespace per module.
What type does a JSON null get?
A null becomes Maybe Value, and an empty array becomes [Value]. Both rely on Data.Aeson's Value type as a safe fallback when no concrete type can be inferred.
Is my data uploaded anywhere?
No — it runs entirely in your browser. Your input never leaves your device and it works offline once loaded.
Is it free?
Yes, completely free with no sign-up and no limits.

People also ask

Does JSON to Haskell generate FromJSON and ToJSON instances?
No. It emits only the record data declarations with a Generic deriving clause. You add the Aeson instances yourself, either by writing them or by using DeriveAnyClass/deriving generically from the Generic instance.
Why are all my record fields prefixed with the type name?
Haskell records share a single field-name namespace per module, so two records with a field called id would clash. Prefixing each field with the lowercased type name (rootId, addressId) keeps them unique within the module.
How do I convert a JSON array at the top level?
Paste it and the tool wraps it in a Root record with a single unRoot field whose type is a list of the inferred element type. You will typically simplify this to a plain type alias or newtype around the list.
Can it tell the difference between Int and Double?
It uses the sample value: a whole number becomes Int and a number with a fractional part becomes Double. Because it only sees one example, change Int to Double manually if the field can ever hold a decimal.
What Haskell text type does it use for strings?
It uses Text from Data.Text and adds the OverloadedStrings pragma and import to the generated header. If you prefer String or ByteString, swap the type after pasting.
How does it name types for nested objects and arrays of objects?
Nested objects are named after their key, capitalized (a city object under address yields Address). For arrays it singularizes the key name to name the element type, so a friends array of objects produces a Friend type.
Is JSON to Haskell free and does it keep my data private?
Yes. It is completely free with no sign-up or limits, and all conversion happens locally in your browser, so the JSON you paste never leaves your device and it works offline once loaded.
Will the generated code compile as-is?
It is a starting point, not a finished module. You will usually need to fill in any Maybe Value or [Value] fallbacks, add Aeson instances, and adjust optionality, but the record structure and imports are ready to paste.

Related tools