Free & open source — no account required

v1.2.5-PRICING-19
Web & Frontend • Engineering Documentation

JSON to Haskell Data Generator

This technical guide provides an in-depth analysis of the json to haskell data engine, best practices for implementation, and data security standards.

JSON to Haskell: Generating Aeson Records with DeriveGeneric

Haskell's aeson library is the standard for JSON in Haskell — it uses GHC's DeriveGeneric extension to derive FromJSON and ToJSON instances automatically from your data type definition. When you have a JSON payload from an API, generating the Haskell record type with the correct field names and Aeson derivations gives you a complete, type-safe decoder with zero boilerplate. The resulting type integrates directly with http-conduit, servant, and any other library that speaks Aeson.

Live Example: API Response to Haskell Record

// Input JSON
{
  "user_id": "usr_4421",
  "username": "haskell_dev",
  "email": "haskell@example.com",
  "score": 9200,
  "is_active": true,
  "bio": null
}

-- Generated Haskell
{-# LANGUAGE DeriveGeneric #-}
module MyApp.Root where

import GHC.Generics (Generic)
import Data.Aeson (FromJSON, ToJSON)

data Root = Root
  { user_id   :: String
  , username  :: String
  , email     :: String
  , score     :: Double
  , is_active :: Bool
  , bio       :: Maybe String
  } deriving (Show, Generic)

instance FromJSON Root
instance ToJSON Root

Idiomatic Haskell: camelCase with fieldLabelModifier

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
module User where

import GHC.Generics (Generic)
import Data.Aeson
import Data.Aeson.Types (Options(..), defaultOptions)
import Data.Text (Text)

-- Haskell convention: camelCase prefixed with type name
data User = User
  { userId   :: Text    -- Text not String (more efficient)
  , username :: Text
  , email    :: Text
  , score    :: Int     -- Int not Double for whole numbers
  , isActive :: Bool
  , bio      :: Maybe Text
  } deriving (Show, Eq, Generic)

-- Map camelCase Haskell fields to snake_case JSON keys
userOptions :: Options
userOptions = defaultOptions
  { fieldLabelModifier = camelToSnake
  }
  where
    camelToSnake [] = []
    camelToSnake (x:xs)
      | x `elem` ['A'..'Z'] = '_' : toLower x : camelToSnake xs
      | otherwise            = x : camelToSnake xs

instance FromJSON User where
  parseJSON = genericParseJSON userOptions

instance ToJSON User where
  toJSON     = genericToJSON userOptions
  toEncoding = genericToEncoding userOptions

-- Or use the aeson-casing package (simpler):
-- import Data.Aeson.Casing (snakeCase)
-- userOptions = defaultOptions { fieldLabelModifier = snakeCase }

Fetching JSON with http-conduit

{-# LANGUAGE OverloadedStrings #-}
module Main where

import Network.HTTP.Simple
import Data.Aeson (eitherDecode)
import qualified Data.ByteString.Lazy as BL
import User (User)

fetchUser :: String -> IO (Either String User)
fetchUser userId = do
  let url = "https://api.example.com/users/" <> userId
  request  <- parseRequest url
  response <- httpLBS request
  let body = getResponseBody response
  return $ eitherDecode body

main :: IO ()
main = do
  result <- fetchUser "usr_4421"
  case result of
    Left  err  -> putStrLn $ "Parse error: " <> err
    Right user -> do
      putStrLn $ "Username: " <> show (username user)
      putStrLn $ "Score: "    <> show (score user)
      case bio user of
        Nothing -> putStrLn "No bio"
        Just b  -> putStrLn $ "Bio: " <> show b

Using with Servant (Type-Safe API Client)

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
module API where

import Servant
import Servant.Client
import User (User)
import Data.Proxy (Proxy(..))

-- Define the API type
type UserAPI
  =    "users" :> Capture "id" String :> Get '[JSON] User
  :<|> "users" :> ReqBody '[JSON] User :> Post '[JSON] User

-- Generate client functions automatically
userAPI :: Proxy UserAPI
userAPI = Proxy

getUser :: String -> ClientM User
createUser :: User -> ClientM User
getUser :<|> createUser = client userAPI

-- Run the client
import Network.HTTP.Client (newManager, defaultManagerSettings)

runClient :: IO ()
runClient = do
  manager <- newManager defaultManagerSettings
  let env = mkClientEnv manager (BaseUrl Http "api.example.com" 80 "")
  result <- runClientM (getUser "usr_4421") env
  case result of
    Left  err  -> print err
    Right user -> print (username user)

Frequently Asked Questions

Why does the generator use String instead of Text? String is Haskell's built-in list of characters — simple but inefficient. Data.Text.Text is a packed UTF-16 representation that's faster and uses less memory. In production Haskell, always use Text for string data. Add {-# LANGUAGE OverloadedStrings #-} so string literals work as both String and Text.

What does deriving Generic do? GHC's DeriveGeneric extension generates a generic representation of your data type. Aeson uses this representation to automatically derive FromJSON/ToJSON instances without requiring you to write any JSON field mapping code. The derived instances match field names exactly — use fieldLabelModifier to map between Haskell camelCase and JSON snake_case.

What's the difference between toJSON and toEncoding? toJSON builds an intermediate Value AST before serializing to bytes — useful for inspecting the JSON value in code. toEncoding streams directly to bytes, skipping the intermediate AST — faster for production use. Always implement both when writing manual instances; the derived version handles both automatically.

Is my JSON sent to a server? No. TypeMorph runs entirely in your browser — none of your data leaves your machine.

Developer FAQ

Is the processing local-only?

Absolutely. TypeMorph operates entirely within your browser's sandbox. We use Web Workers for high-performance computation without ever transmitting your JSON, SQL, or API data to a remote server.

Can I use this for enterprise projects?

Yes. The tool is designed for professional software engineers who require GDPR compliance and data privacy. It is trusted by developers at top-tier startups and financial institutions.