1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
|
{-# LANGUAGE DefaultSignatures, EmptyDataDecls, FlexibleInstances,
FunctionalDependencies, KindSignatures, OverlappingInstances,
ScopedTypeVariables, TypeOperators, UndecidableInstances, ViewPatterns #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
-- |
-- Module: Data.Aeson.Types.Generic
-- Copyright: (c) 2012 Bryan O'Sullivan
-- (c) 2011 Bas Van Dijk
-- (c) 2011 MailRank, Inc.
-- License: Apache
-- Maintainer: Bryan O'Sullivan <bos@serpentine.com>
-- Stability: experimental
-- Portability: portable
--
-- Types for working with JSON data.
module Data.Aeson.Types.Generic ( ) where
import Control.Applicative ((<*>), (<$>), (<|>), pure)
import Control.Monad.ST (ST)
import Data.Aeson.Types.Class
import Data.Aeson.Types.Internal
import Data.Bits (shiftR)
import Data.DList (DList, toList)
import Data.Monoid (mappend)
import Data.Text (pack, unpack)
import GHC.Generics
import qualified Data.HashMap.Strict as H
import qualified Data.Text as T
import qualified Data.Vector as V
import qualified Data.Vector.Mutable as VM
--------------------------------------------------------------------------------
-- Generic toJSON
instance (GToJSON a) => GToJSON (M1 i c a) where
gToJSON = gToJSON . unM1
{-# INLINE gToJSON #-}
instance (ToJSON a) => GToJSON (K1 i a) where
gToJSON = toJSON . unK1
{-# INLINE gToJSON #-}
instance GToJSON U1 where
gToJSON _ = emptyArray
{-# INLINE gToJSON #-}
instance (ConsToJSON a) => GToJSON (C1 c a) where
gToJSON = consToJSON . unM1
{-# INLINE gToJSON #-}
instance ( GProductToValues a, GProductToValues b
, ProductSize a, ProductSize b) => GToJSON (a :*: b) where
gToJSON p = Array $ V.create $ do
mv <- VM.unsafeNew lenProduct
gProductToValues mv 0 lenProduct p
return mv
where
lenProduct = unTagged2 (productSize :: Tagged2 (a :*: b) Int)
{-# INLINE gToJSON #-}
instance (GObject a, GObject b) => GToJSON (a :+: b) where
gToJSON (L1 x) = Object $ gObject x
gToJSON (R1 x) = Object $ gObject x
{-# INLINE gToJSON #-}
--------------------------------------------------------------------------------
class ConsToJSON f where consToJSON :: f a -> Value
class ConsToJSON' b f where consToJSON' :: Tagged b (f a -> Value)
newtype Tagged s b = Tagged {unTagged :: b}
instance (IsRecord f b, ConsToJSON' b f) => ConsToJSON f where
consToJSON = unTagged (consToJSON' :: Tagged b (f a -> Value))
{-# INLINE consToJSON #-}
instance (GRecordToPairs f) => ConsToJSON' True f where
consToJSON' = Tagged (object . toList . gRecordToPairs)
{-# INLINE consToJSON' #-}
instance GToJSON f => ConsToJSON' False f where
consToJSON' = Tagged gToJSON
{-# INLINE consToJSON' #-}
--------------------------------------------------------------------------------
class GRecordToPairs f where
gRecordToPairs :: f a -> DList Pair
instance (GRecordToPairs a, GRecordToPairs b) => GRecordToPairs (a :*: b) where
gRecordToPairs (a :*: b) = gRecordToPairs a `mappend` gRecordToPairs b
{-# INLINE gRecordToPairs #-}
instance (Selector s, GToJSON a) => GRecordToPairs (S1 s a) where
gRecordToPairs m1 = pure (pack (selName m1), gToJSON (unM1 m1))
{-# INLINE gRecordToPairs #-}
--------------------------------------------------------------------------------
class GProductToValues f where
gProductToValues :: VM.MVector s Value -> Int -> Int -> f a -> ST s ()
instance (GProductToValues a, GProductToValues b) => GProductToValues (a :*: b) where
gProductToValues mv ix len (a :*: b) = do gProductToValues mv ix lenL a
gProductToValues mv ixR lenR b
where
lenL = len `shiftR` 1
ixR = ix + lenL
lenR = len - lenL
{-# INLINE gProductToValues #-}
instance (GToJSON a) => GProductToValues a where
gProductToValues mv ix _ = VM.unsafeWrite mv ix . gToJSON
{-# INLINE gProductToValues #-}
--------------------------------------------------------------------------------
class GObject f where
gObject :: f a -> Object
instance (GObject a, GObject b) => GObject (a :+: b) where
gObject (L1 x) = gObject x
gObject (R1 x) = gObject x
{-# INLINE gObject #-}
instance (Constructor c, GToJSON a, ConsToJSON a) => GObject (C1 c a) where
gObject = H.singleton (pack $ conName (undefined :: t c a p)) . gToJSON
{-# INLINE gObject #-}
--------------------------------------------------------------------------------
-- Generic parseJSON
instance (GFromJSON a) => GFromJSON (M1 i c a) where
gParseJSON = fmap M1 . gParseJSON
{-# INLINE gParseJSON #-}
instance (FromJSON a) => GFromJSON (K1 i a) where
gParseJSON = fmap K1 . parseJSON
{-# INLINE gParseJSON #-}
instance GFromJSON U1 where
gParseJSON v
| isEmptyArray v = pure U1
| otherwise = typeMismatch "unit constructor (U1)" v
{-# INLINE gParseJSON #-}
instance (ConsFromJSON a) => GFromJSON (C1 c a) where
gParseJSON = fmap M1 . consParseJSON
{-# INLINE gParseJSON #-}
instance ( GFromProduct a, GFromProduct b
, ProductSize a, ProductSize b) => GFromJSON (a :*: b) where
gParseJSON (Array arr)
| lenArray == lenProduct = gParseProduct arr 0 lenProduct
| otherwise =
fail $ "When expecting a product of " ++ show lenProduct ++
" values, encountered an Array of " ++ show lenArray ++
" elements instead"
where
lenArray = V.length arr
lenProduct = unTagged2 (productSize :: Tagged2 (a :*: b) Int)
gParseJSON v = typeMismatch "product (:*:)" v
{-# INLINE gParseJSON #-}
instance (GFromSum a, GFromSum b) => GFromJSON (a :+: b) where
gParseJSON (Object (H.toList -> [keyVal@(key, _)])) =
case gParseSum keyVal of
Nothing -> notFound $ unpack key
Just p -> p
gParseJSON v = typeMismatch "sum (:+:)" v
{-# INLINE gParseJSON #-}
notFound :: String -> Parser a
notFound key = fail $ "The key \"" ++ key ++ "\" was not found"
{-# INLINE notFound #-}
--------------------------------------------------------------------------------
class ConsFromJSON f where consParseJSON :: Value -> Parser (f a)
class ConsFromJSON' b f where consParseJSON' :: Tagged b (Value -> Parser (f a))
instance (IsRecord f b, ConsFromJSON' b f) => ConsFromJSON f where
consParseJSON = unTagged (consParseJSON' :: Tagged b (Value -> Parser (f a)))
{-# INLINE consParseJSON #-}
instance (GFromRecord f) => ConsFromJSON' True f where
consParseJSON' = Tagged parseRecord
where
parseRecord (Object obj) = gParseRecord obj
parseRecord v = typeMismatch "record (:*:)" v
{-# INLINE consParseJSON' #-}
instance (GFromJSON f) => ConsFromJSON' False f where
consParseJSON' = Tagged gParseJSON
{-# INLINE consParseJSON' #-}
--------------------------------------------------------------------------------
class GFromRecord f where
gParseRecord :: Object -> Parser (f a)
instance (GFromRecord a, GFromRecord b) => GFromRecord (a :*: b) where
gParseRecord obj = (:*:) <$> gParseRecord obj <*> gParseRecord obj
{-# INLINE gParseRecord #-}
instance (Selector s, GFromJSON a) => GFromRecord (S1 s a) where
gParseRecord = maybe (notFound key) gParseJSON . H.lookup (T.pack key)
where
key = selName (undefined :: t s a p)
{-# INLINE gParseRecord #-}
--------------------------------------------------------------------------------
class ProductSize f where
productSize :: Tagged2 f Int
newtype Tagged2 (s :: * -> *) b = Tagged2 {unTagged2 :: b}
instance (ProductSize a, ProductSize b) => ProductSize (a :*: b) where
productSize = Tagged2 $ unTagged2 (productSize :: Tagged2 a Int) +
unTagged2 (productSize :: Tagged2 b Int)
instance ProductSize (S1 s a) where
productSize = Tagged2 1
--------------------------------------------------------------------------------
class GFromProduct f where
gParseProduct :: Array -> Int -> Int -> Parser (f a)
instance (GFromProduct a, GFromProduct b) => GFromProduct (a :*: b) where
gParseProduct arr ix len = (:*:) <$> gParseProduct arr ix lenL
<*> gParseProduct arr ixR lenR
where
lenL = len `shiftR` 1
ixR = ix + lenL
lenR = len - lenL
{-# INLINE gParseProduct #-}
instance (GFromJSON a) => GFromProduct (S1 s a) where
gParseProduct arr ix _ = gParseJSON $ V.unsafeIndex arr ix
{-# INLINE gParseProduct #-}
--------------------------------------------------------------------------------
class GFromSum f where
gParseSum :: Pair -> Maybe (Parser (f a))
instance (GFromSum a, GFromSum b) => GFromSum (a :+: b) where
gParseSum keyVal = (fmap L1 <$> gParseSum keyVal) <|>
(fmap R1 <$> gParseSum keyVal)
{-# INLINE gParseSum #-}
instance (Constructor c, GFromJSON a, ConsFromJSON a) => GFromSum (C1 c a) where
gParseSum (key, value)
| key == pack (conName (undefined :: t c a p)) = Just $ gParseJSON value
| otherwise = Nothing
{-# INLINE gParseSum #-}
--------------------------------------------------------------------------------
class IsRecord (f :: * -> *) b | f -> b
data True
data False
instance (IsRecord f b) => IsRecord (f :*: g) b
instance IsRecord (M1 S NoSelector f) False
instance (IsRecord f b) => IsRecord (M1 S c f) b
instance IsRecord (K1 i c) True
instance IsRecord U1 False
--------------------------------------------------------------------------------
|