Json in Haskell

In Haskell, we can use the aeson library for JSON encoding and decoding. This library provides built-in support for working with JSON data, including custom data types.

First, let’s import the necessary modules:

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}

import Data.Aeson
import Data.ByteString.Lazy (ByteString)
import qualified Data.ByteString.Lazy as B
import Data.Text (Text)
import qualified Data.Text as T
import Data.Map (Map)
import qualified Data.Map as Map

We’ll define two data types to demonstrate encoding and decoding of custom types:

data Response1 = Response1
    { page1 :: Int
    , fruits1 :: [Text]
    } deriving (Show)

data Response2 = Response2
    { page2 :: Int
    , fruits2 :: [Text]
    } deriving (Show)

instance ToJSON Response1 where
    toJSON Response1{..} = object
        [ "Page" .= page1
        , "Fruits" .= fruits1
        ]

instance FromJSON Response1 where
    parseJSON = withObject "Response1" $ \v -> Response1
        <$> v .: "Page"
        <*> v .: "Fruits"

instance ToJSON Response2 where
    toJSON Response2{..} = object
        [ "page" .= page2
        , "fruits" .= fruits2
        ]

instance FromJSON Response2 where
    parseJSON = withObject "Response2" $ \v -> Response2
        <$> v .: "page"
        <*> v .: "fruits"

Now, let’s look at encoding basic data types to JSON:

main :: IO ()
main = do
    let bolB = encode True
    B.putStrLn bolB

    let intB = encode (1 :: Int)
    B.putStrLn intB

    let fltB = encode (2.34 :: Float)
    B.putStrLn fltB

    let strB = encode ("gopher" :: Text)
    B.putStrLn strB

For slices and maps, which encode to JSON arrays and objects:

    let slcD = ["apple", "peach", "pear"] :: [Text]
    let slcB = encode slcD
    B.putStrLn slcB

    let mapD = Map.fromList [("apple", 5), ("lettuce", 7)] :: Map Text Int
    let mapB = encode mapD
    B.putStrLn mapB

Now, let’s encode our custom data types:

    let res1D = Response1 1 ["apple", "peach", "pear"]
    let res1B = encode res1D
    B.putStrLn res1B

    let res2D = Response2 1 ["apple", "peach", "pear"]
    let res2B = encode res2D
    B.putStrLn res2B

For decoding JSON data into Haskell values:

    let byt = "{\"num\":6.13,\"strs\":[\"a\",\"b\"]}" :: ByteString
    case decode byt of
        Just dat -> do
            putStrLn $ "Decoded: " ++ show (dat :: Map Text Value)
            case Map.lookup "num" dat of
                Just (Number n) -> print n
                _ -> putStrLn "Failed to extract num"
            case Map.lookup "strs" dat of
                Just (Array arr) -> print $ head arr
                _ -> putStrLn "Failed to extract strs"
        Nothing -> putStrLn "Failed to decode"

    let str = "{\"page\": 1, \"fruits\": [\"apple\", \"peach\"]}" :: ByteString
    case decode str of
        Just res -> do
            print (res :: Response2)
            print $ head $ fruits2 res
        Nothing -> putStrLn "Failed to decode Response2"

In Haskell, we don’t have a direct equivalent to Go’s json.NewEncoder. However, we can achieve similar functionality by using B.hPutStrLn:

    let d = Map.fromList [("apple", 5), ("lettuce", 7)] :: Map Text Int
    B.hPutStrLn stdout $ encode d

This Haskell code demonstrates JSON encoding and decoding using the aeson library. It covers basic data types, custom types, and working with more complex nested structures. The aeson library provides a powerful and flexible way to work with JSON in Haskell, with support for automatic derivation of JSON instances for many types.