msgpack-aeson-0.1.0.0: src/Data/MessagePack/Aeson.hs
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LambdaCase #-}
-- | Aeson bridge for MessagePack
module Data.MessagePack.Aeson (
-- * Conversion functions
toAeson, fromAeson,
-- * MessagePack instance for Aeson.Value
-- $msgpackInstance
-- * ToJSON and FromJSON instance for MessagePack.Object
-- $aesonInstances
-- * Wrapper instances
AsMessagePack(..),
AsAeson(..),
-- * Utility functions
packAeson, unpackAeson,
decodeMessagePack, encodeMessagePack,
) where
import Control.Applicative
import Control.Arrow
import Control.DeepSeq
import Data.Aeson as A
import qualified Data.ByteString.Lazy as L
import Data.Data
import qualified Data.HashMap.Strict as HM
import Data.Maybe
import Data.MessagePack as MP
import Data.Scientific
import qualified Data.Text.Encoding as T
import qualified Data.Vector as V
-- | Convert MessagePack Object to Aeson Value.
-- If the value unable to convert, it returns Nothing
toAeson :: MP.Object -> Maybe Value
toAeson = \case
ObjectNil -> Just Null
ObjectBool b -> Just $ Bool b
ObjectInt n -> Just $ Number $ fromIntegral n
ObjectFloat f -> Just $ Number $ realToFrac f
ObjectDouble d -> Just $ Number $ realToFrac d
ObjectStr t -> Just $ String t
ObjectBin b -> String <$> either (const Nothing) Just (T.decodeUtf8' b)
ObjectArray v -> Array <$> V.mapM toAeson v
ObjectMap m ->
A.Object . HM.fromList . V.toList
<$> V.mapM (\(k, v) -> (,) <$> fromObject k <*> toAeson v) m
ObjectExt _ _ -> Nothing
-- | Convert Aeson Value to MessagePack Object
fromAeson :: Value -> MP.Object
fromAeson = \case
Null -> ObjectNil
Bool b -> ObjectBool b
Number s ->
case floatingOrInteger s of
Left f -> ObjectDouble f
Right n -> ObjectInt n
String t -> ObjectStr t
Array v -> ObjectArray $ V.map fromAeson v
A.Object o -> ObjectMap $ V.fromList $ map (toObject *** fromAeson) $ HM.toList o
-- $msgpackInstance
-- > instance MessagePack Value
instance MessagePack Value where
fromObject = toAeson
toObject = fromAeson
-- $aesonInstances
-- > instance ToJSON Object
-- > instance FromJSON Object
instance ToJSON MP.Object where
-- When fail to convert, it returns `Null`
toJSON = fromMaybe Null .toAeson
instance FromJSON MP.Object where
parseJSON = return . fromAeson
-- | Wrapper for using Aeson values as MessagePack value.
newtype AsMessagePack a = AsMessagePack { getAsMessagePack :: a }
deriving (Eq, Ord, Show, Read, Functor, Data, Typeable, NFData)
instance (FromJSON a, ToJSON a) => MessagePack (AsMessagePack a) where
fromObject o = AsMessagePack <$> (fromJSON' =<< toAeson o)
toObject = fromAeson . toJSON . getAsMessagePack
-- | Wrapper for using MessagePack values as Aeson value.
newtype AsAeson a = AsAeson { getAsAeson :: a }
deriving (Eq, Ord, Show, Read, Functor, Data, Typeable, NFData)
instance MessagePack a => ToJSON (AsAeson a) where
toJSON = fromMaybe Null . toAeson . toObject . getAsAeson
instance MessagePack a => FromJSON (AsAeson a) where
parseJSON = maybe empty (return . AsAeson) . fromObject . fromAeson
-- | Pack Aeson value to MessagePack binary
packAeson :: ToJSON a => a -> L.ByteString
packAeson = pack . toJSON
-- | Unpack Aeson value from MessagePack binary
unpackAeson :: FromJSON a => L.ByteString -> Maybe a
unpackAeson b = fromJSON' =<< unpack b
-- | Encode MessagePack value to JSON
encodeMessagePack :: MessagePack a => a -> L.ByteString
encodeMessagePack = encode . toJSON . AsAeson
-- | Decode MessagePack value from JSON
decodeMessagePack :: MessagePack a => L.ByteString -> Maybe a
decodeMessagePack b = getAsAeson <$> (fromJSON' =<< decode b)
fromJSON' :: FromJSON a => Value -> Maybe a
fromJSON' = resultToMaybe . fromJSON
resultToMaybe :: Result a -> Maybe a
resultToMaybe = \case
Success a -> Just a
_ -> Nothing