arch-hs-0.0.0.0: src/Data/Aeson/Ext.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# OPTIONS_HADDOCK hide #-}
module Data.Aeson.Ext
( generateJSONInstance,
parseJSONDrop,
toJSONDrop,
)
where
import Data.Aeson
import Data.Aeson.Types
import GHC.Generics (Generic, Rep)
import Language.Haskell.TH
dropSize :: Type -> Q Int
dropSize (ConT n) = do
info <- reify n
case info of
(TyConI (DataD _ _ [] _ cons _)) -> go cons
(TyConI (NewtypeD _ _ _ _ con _)) -> go [con]
_ -> fail "Unsupported."
where
-- Try only the first record constructor
rec (x : _) = case x of
(RecC _ f) -> f
_ -> fail "Unsupported"
rec [] = fail "Unsupported"
go cons = do
let fields = rec cons
-- Find prefix from the first field
let (name, _, _) = head fields
let str = nameBase name
return . (+ 1) $ length str - length (dropWhile (/= '_') str)
dropSize _ = fail "Unsupported"
generateToJSONInstance :: Name -> DecQ
generateToJSONInstance targetType =
conT targetType >>= dropSize >>= \s -> instanceD (cxt []) (appT (conT ''ToJSON) (conT targetType)) [gen_f $ fromIntegral s]
where
gen_f s = funD (mkName "toJSON") [clause [] (normalB (varE 'toJSONDrop `appE` litE (integerL s))) []]
generateFromJSONInstance :: Name -> DecQ
generateFromJSONInstance targetType =
conT targetType >>= dropSize >>= \s -> instanceD (cxt []) (appT (conT ''FromJSON) (conT targetType)) [gen_f $ fromIntegral s]
where
gen_f s = funD (mkName "parseJSON") [clause [] (normalB (varE 'parseJSONDrop `appE` litE (integerL s))) []]
generateJSONInstance :: Name -> Q [Dec]
generateJSONInstance name = do
from <- generateFromJSONInstance name
to <- generateToJSONInstance name
return [from, to]
toJSONDrop ::
forall a.
(Generic a, GToJSON Zero (Rep a)) =>
Int ->
a ->
Value
toJSONDrop prefix =
genericToJSON defaultOptions {fieldLabelModifier = drop prefix, omitNothingFields = True, sumEncoding = UntaggedValue}
parseJSONDrop ::
forall a.
(Generic a, GFromJSON Zero (Rep a)) =>
Int ->
Value ->
Parser a
parseJSONDrop prefix = genericParseJSON defaultOptions {fieldLabelModifier = drop prefix, sumEncoding = UntaggedValue}