Free & open source — no account required
Free & open source — no account required
This technical guide provides an in-depth analysis of the json to haskell data engine, best practices for implementation, and data security standards.
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.
// 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
{-# 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 }
{-# 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
{-# 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)
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.
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.