{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE ScopedTypeVariables #-} ----------------------------------------------------------------------------- -- | -- Module : Generics.Regular.Views -- Copyright : (c) 2010 Chris Eidhof -- License : BSD3 -- -- Maintainer : chris@eidhof.nl -- Stability : experimental -- Portability : non-portable -- -- Generic generation of 'JSON' values. Note that the generic -- functions are only defined for record datatypes that contain -- a single constructor. -- -- The code that is generated by 'gto' should be parseable by 'gfrom'. ----------------------------------------------------------------------------- module Generics.Regular.JSON (gfrom, gto, GJSON) where import Text.JSON import Generics.Regular import Generics.Regular.Extras import Control.Applicative import Data.List (unionBy) -- | The function 'gfrom' tries to parse a 'JSValue'. The 'Result' datatype is used for error-messages if parsing fails. gfrom :: (Regular a, GJSON (PF a)) => JSValue -> Result a gfrom = fmap to . gfrom' -- | The function 'gto' generates a 'JSValue' for all types that are an instance of 'GJSON'. gto :: (Regular a, GJSON (PF a)) => a -> JSValue gto = gto' . from -- | This class is used for both generation and parsing of 'JSON'. class GJSON f where gto' :: f a -> JSValue gfrom' :: JSValue -> Result (f a) instance GJSON U where gto' U = JSNull gfrom' JSNull = Ok U gfrom' _ = Error "could not parse U" instance JSON a => GJSON (K a) where gto' (K x) = showJSON x gfrom' x = K <$> readJSON x instance (GJSON (S s f), GJSON g) => GJSON ((S s f) :*: g) where gto' (a :*: b) = merge (gto' a) (gto' b) gfrom' x = do (:*:) <$> gfrom' x <*> gfrom' x instance (Selector s, GJSON f) => GJSON (S s f) where gto' s@(S x) = JSObject $ toJSObject [(humanReadable $ selName s, gto' x)] gfrom' (JSObject obj) = let s = humanReadable $ selName (undefined :: S s f x) in case valFromObj s obj of Ok x -> S <$> gfrom' x Error e -> Error e gfrom' x = Error $ "Expected json object, got " ++ show x instance GJSON f => GJSON (C c f) where gto' (C x) = gto' x gfrom' x = C <$> gfrom' x merge :: JSValue -> JSValue -> JSValue merge (JSObject l) (JSObject r) = JSObject (toJSObject $ mergeList (fromJSObject l) (fromJSObject r)) merge _ _ = error "Cannot merge objects." mergeList :: [(String, JSValue)] -> [(String, JSValue)] -> [(String, JSValue)] mergeList = unionBy (\x y -> fst x == fst y)