您可以使用类似于Higher Kinded Data的方法来实现这一点.
首先,一些进口:
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE UndecidableInstances #-}
module Example where
import Data.Functor.Identity
import Data.Proxy
import Data.UUID
import Data.Aeson
import GHC.Generics
然后,使用更高的同类类型参数定义记录:
data Record f = Record { recordId :: f UUID, recordValue :: String } deriving (Generic)
Identity
Functor给你的是这个记录的一个变体,always has是一个ID.
type RecordWithId = Record Identity
使用Maybe
会给出一个ID为optional的变量.
type RecordWithOptionalId = Record Maybe
Proxy
可以用作具有单个不感兴趣的"单位"值的函数值.(并且没有包装类型的值).这使我们可以为没有ID的Record
创建一个类型.
type RecordWithoutId = Record (Proxy)
我们的Record
可以得到Show
.
deriving instance (Show (f UUID)) => Show (Record f)
正如您所预期的那样,需要在Aeson
instances中传递omitNothingFields = True
和allowOmitedFields = True
才能解析RecordWithoutId
.这确实需要一个版本的Aeson&>=2.2.0.0(在 compose 本文时,该版本比最新的Stackage Snapshot更新).如果此版本绑定不适用于您,您可能可以手动实现Aeson实例.
instance (ToJSON (f UUID)) => ToJSON (Record f) where
toJSON = genericToJSON defaultOptions { omitNothingFields = True, allowOmitedFields = True }
instance (FromJSON (f UUID)) => FromJSON (Record f) where
parseJSON = genericParseJSON defaultOptions { omitNothingFields = True, allowOmitedFields = True }
使用ID编码值:
ghci> import qualified Data.ByteString.Lazy.Char8 as BL
ghci> BL.putStrLn $ encode (Record {recordId = Identity nil, recordValue = "value" })
{"recordId":"00000000-0000-0000-0000-000000000000","recordValue":"value"}
对没有ID的值进行编码:
ghci> BL.putStrLn $ encode (Record {recordId = Proxy, recordValue = "value" })
{"recordValue":"value"}
对没有ID的值进行解码
ghci> decode "{\"recordValue\":\"value\"}" :: Maybe RecordWithoutId
Just (Record {recordId = Proxy, recordValue = "value"})