[Haskell-cafe] Text.JSON idiomatic use

ntupel ntupel at googlemail.com
Fri Sep 12 18:24:54 EDT 2008


As a follow up to my previous JSON serialization post I came up with a
first draft of some simple record type serialization/deserialization. 

What I would like to know is, whether this is the right approach or what
better ways there are to make a custom data type an instance of class
JSON. Any chance to reduce the amount of boilerplate required to do
this? I would be grateful for any feedback (also general style comments
are much appreciated).

Many thanks!




module Test where

import Text.JSON

data Message =
    Error {
        event   :: String,
        channel :: String,
        id      :: String,
        cause   :: String,
        message :: String}
  | Join {
        event   :: String,
        channel :: String,
        id      :: String,
        name    :: String}
  | Leave {
        event   :: String,
        channel :: String,
        id      :: String,
        really  :: Bool}
  deriving (Eq, Show, Read)

asJSString :: String -> JSValue
asJSString = JSString . toJSString

asString :: JSValue -> String
asString (JSString s) = fromJSString s

asBool :: JSValue -> Bool
asBool (JSBool b) = b


showErrorJSON, showJoinJSON, showLeaveJSON :: Message -> JSValue

showErrorJSON (Test.Error evt cha id cau msg) =
    showJSON $ toJSObject [("event", evt), ("channel", cha), ("id", id), ("cause", cau), ("message", msg)]

showJoinJSON (Join evt cha id nme) =
    showJSON $ toJSObject [("event", evt), ("channel", cha), ("id", id), ("name", nme)]

showLeaveJSON (Leave evt cha id rly) =
    showJSON $ toJSObject [("event", asJSString evt), ("channel", asJSString cha), ("id", asJSString id), ("really", JSBool rly)]


createMessage, readErrorJSON, readJoinJSON, readLeaveJSON :: [(String, JSValue)] -> Maybe Message

readErrorJSON xs = do
    evt <- lookup "event" xs
    cha <- lookup "channel" xs
    id  <- lookup "id" xs
    cau <- lookup "cause" xs
    msg <- lookup "message" xs
    Just (Test.Error (asString evt) (asString cha) (asString id) (asString cau) (asString msg))

readJoinJSON xs = do
    evt <- lookup "event" xs
    cha <- lookup "channel" xs
    id  <- lookup "id" xs
    nme <- lookup "name" xs
    Just (Join (asString evt) (asString cha) (asString id) (asString nme))

readLeaveJSON xs = do
    evt <- lookup "event" xs
    cha <- lookup "channel" xs
    id  <- lookup "id" xs
    rly <- lookup "really" xs
    Just (Leave (asString evt) (asString cha) (asString id) (asBool rly))

createMessage obj = do
    evt <- lookup "event" obj
    case asString evt of
        "/error"  -> readErrorJSON obj
        "/me/add" -> readJoinJSON obj
        "/me/remove" -> readLeaveJSON obj
        _ -> Nothing

instance JSON Message where
    showJSON x@(Test.Join    _ _ _ _) = showJoinJSON x
    showJSON x@(Test.Leave   _ _ _ _) = showLeaveJSON x
    showJSON x@(Test.Error _ _ _ _ _) = showErrorJSON x

    readJSON (JSObject o) = 
        case createMessage . fromJSObject $ o of
            Just m  -> Ok m
            Nothing -> Text.JSON.Error "Parsing failed."

    readJSON _ = Text.JSON.Error "Records must be JSObjects"




More information about the Haskell-Cafe mailing list