{-# LANGUAGE DataKinds #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE TypeOperators #-} module Data.Json ( -- * DSL to define JSON structure JsonSpec(..), FieldSpec(..), FieldKey, P.reqKey, P.optKey, P.TypedKey , (.=), (.=?) -- * DSL to define JSON structure for sum types , JsonSumSpec(..), (P..->), (P.<||>), (S..<-) , -- * Make parsers and serialisers from spec makeParser, makeSerialiser, makeSumParser, makeSumSerialiser , S.ToJson(..), P.JsonReadable(..) , -- * Run parsers / serialisers P.parseJsonBs, P.parseJsonBsl, P.parseJsonT , S.serialiseJsonBs, S.serialiseJsonBsl, S.serialiseJsonT ) where import Data.HVect import Data.Typeable import qualified Data.Json.Serialiser as S import qualified Data.Json.Parser as P -- | Describes JSON parsing and serialisation of a Haskell type data JsonSpec k (ts :: [*]) = JsonSpec { j_constr :: !(HVectElim ts k) , j_fields :: !(FieldSpec k ts) } -- | Describes JSON parsing and serialisation of a list of fields data FieldSpec k (ts :: [*]) where EmptySpec :: FieldSpec k '[] (:+:) :: (S.ToJson t, P.JsonReadable t, Typeable t) => !(FieldKey k t) -> !(FieldSpec k ts) -> FieldSpec k (t ': ts) infixr 5 :+: -- | Describes a json key data FieldKey k t = FieldKey { fk_tk :: !(P.TypedKey t) , fk_sk :: !(S.SpecKey k t) } -- | Construct a 'FieldKey' mapping a json key to a getter function (.=) :: (S.ToJson t, P.JsonReadable t, Typeable t) => P.TypedKey t -> (k -> t) -> FieldKey k t tk .= getter = FieldKey tk ((P.typedKeyKey tk) S..: getter) {-# INLINE (.=) #-} -- | Construct a 'FieldKey' mapping a json key to a getter function of -- a 'Maybe' type. This allows to omit the key when generating json instead of -- setting it to null. (.=?) :: (S.ToJson t, P.JsonReadable t, Typeable t) => P.TypedKey (Maybe t) -> (k -> Maybe t) -> FieldKey k (Maybe t) tk .=? getter = FieldKey tk ((P.typedKeyKey tk) S..:? getter) {-# INLINE (.=?) #-} -- | Construct a 'P.Parser' from 'JsonSpec' to implement 'P.JsonReadable' instances makeParser :: JsonSpec k ts -> P.Parser k makeParser spec = P.runParseSpec $ (j_constr spec) P.:$: (mkObjSpec $ j_fields spec) {-# INLINE makeParser #-} mkObjSpec :: FieldSpec k ts -> P.ObjSpec ts mkObjSpec EmptySpec = P.ObjSpecNil mkObjSpec (FieldKey k _ :+: xs) = k P.:&&: mkObjSpec xs -- | Construct a function from 'JsonSpec' to implement 'S.ToJson' instances makeSerialiser :: JsonSpec k ts -> k -> S.Value makeSerialiser spec = S.runSerSpec (S.SingleConstr $ mkSerSpec (j_fields spec)) {-# INLINE makeSerialiser #-} mkSerSpec :: FieldSpec k ts -> S.SerObjSpec k ts mkSerSpec EmptySpec = S.SerObjSpecNil mkSerSpec (FieldKey _ getter :+: xs) = getter S.:&&&: mkSerSpec xs -- | Describes JSON parsing and serialisation of a Haskell sum type. Currently -- the library can only guarantee matching parsers/serialisers for -- non-sum types using 'JsonSpec'. data JsonSumSpec k = JsonSumSpec { js_parser :: !P.ParseSpec k , js_serialiser :: !(k -> S.KeyedSerialiser k) } -- | Construct a 'P.Parser' from 'JsonSumSpec' to implement 'P.JsonReadable' instances makeSumParser :: JsonSumSpec k -> P.Parser k makeSumParser = P.runParseSpec . js_parser {-# INLINE makeSumParser #-} -- | Construct a function from 'JsonSumSpec' to implement 'S.ToJson' instances makeSumSerialiser :: JsonSumSpec k -> k -> S.Value makeSumSerialiser s = S.runSerSpec (S.MultiConstr (js_serialiser s)) {-# INLINE makeSumSerialiser #-}