on master help sign in
Merge
commit6b68a10989a9991e0f14d5eb6942f92182c2a36e authorBryan O'Sullivan <bos@serpentine.com> dateWed 30 Nov 2011, 11:02 (15 years ago) parent96ac650e75fc73
17 files changed+2392−1310
Data/Aeson.hs+22−6 Data/Aeson/Encode.hs+3−3 Data/Aeson/Functions.hs+26−22 Data/Aeson/Generic.hs+311−7 Data/Aeson/Parser.hs+66−13 Data/Aeson/TH.hs+285−130 Data/Aeson/Types.hs+6−1085 Data/Aeson/Types/Class.hs+776 Data/Aeson/Types/Generic.hs+284 Data/Aeson/Types/Internal.hs+222 README.markdown+5−5 aeson.cabal+41−14 benchmarks/AesonCompareAutoInstances.hs+198 benchmarks/AesonTuples.hs+45 benchmarks/Makefile+1−1 tests/Makefile−9 tests/Properties.hs+101−15
Data/Aeson.hs+22−6
@@ -2,7 +2,7 @@
22-- Module: Data.Aeson
33-- Copyright: (c) 2011 MailRank, Inc.
44-- License: Apache
5−-- Maintainer: Bryan O'Sullivan <bos@mailrank.com>
5+-- Maintainer: Bryan O'Sullivan <bos@serpentine.com>
66-- Stability: experimental
77-- Portability: portable
88--
@@ -12,8 +12,11 @@
1212
1313module Data.Aeson
1414 (
15+ -- * Encoding and decoding
16+ decode
17+ , encode
1518 -- * Core JSON types
16− Value(..)
19+ , Value(..)
1720 , Array
1821 , Object
1922 -- * Convenience types
@@ -27,12 +30,25 @@ module Data.Aeson
2730 , (.=)
2831 , (.:)
2932 , (.:?)
33+ , (.:/)
3034 , object
31− -- * Encoding and parsing
32− , encode
35+ -- * Parsing
3336 , json
3437 ) where
3538
36−import Data.Aeson.Encode
37−import Data.Aeson.Parser
39+import Data.Aeson.Encode (encode)
40+import Data.Aeson.Parser (json)
3841import Data.Aeson.Types
42+import qualified Data.ByteString.Lazy as L
43+import qualified Data.Attoparsec.Lazy as L
44+
45+-- | Efficiently deserialize a JSON value from a lazy 'L.ByteString'.
46+-- If this fails due to incomplete or invalid input, 'Nothing' is
47+-- returned.
48+decode :: (FromJSON a) => L.ByteString -> Maybe a
49+decode s = case L.parse json s of
50+ L.Done _ v -> case fromJSON v of
51+ Success a -> Just a
52+ _ -> Nothing
53+ _ -> Nothing
54+{-# INLINE decode #-}
Data/Aeson/Encode.hs+3−3
@@ -4,7 +4,7 @@
44-- Module: Data.Aeson.Encode
55-- Copyright: (c) 2011 MailRank, Inc.
66-- License: Apache
7−-- Maintainer: Bryan O'Sullivan <bos@mailrank.com>
7+-- Maintainer: Bryan O'Sullivan <bos@serpentine.com>
88-- Stability: experimental
99-- Portability: portable
1010--
@@ -25,7 +25,7 @@ import Data.Monoid (mappend)
2525import Numeric (showHex)
2626import Blaze.Text (double, integral)
2727import qualified Data.ByteString.Lazy.Char8 as L
28−import qualified Data.Map as M
28+import qualified Data.HashMap.Strict as H
2929import qualified Data.Text as T
3030import qualified Data.Vector as V
3131
@@ -42,7 +42,7 @@ fromValue (Array v)
4242 V.foldr f (fromChar ']') (V.unsafeTail v)
4343 where f a z = fromChar ',' `mappend` fromValue a `mappend` z
4444fromValue (Object m) =
45− case M.toList m of
45+ case H.toList m of
4646 (x:xs) -> fromChar '{' `mappend`
4747 one x `mappend`
4848 foldr f (fromChar '}') xs
Data/Aeson/Functions.hs+26−22
@@ -1,15 +1,14 @@
11module Data.Aeson.Functions
2− (
3− hashMap
4− , mapHash
5− , transformMap
2+ ( mapHashKeyVal
3+ , hashMapKey
4+ , mapKeyVal
5+ , mapKey
66 -- * String conversions
77 , decode
88 , strict
99 , lazy
1010 ) where
1111
12−import Control.Arrow ((***), first)
1312import Data.Hashable (Hashable)
1413import Data.Text (Text)
1514import Data.Text.Encoding (decodeUtf8, encodeUtf8)
@@ -18,23 +17,28 @@ import qualified Data.ByteString.Lazy as L
1817import qualified Data.HashMap.Strict as H
1918import qualified Data.Map as M
2019
21−-- | Transform one map into another. The ordering of keys must be
22−-- preserved by the key transformation function.
23−transformMap :: (Ord k2) => (k1 -> k2) -> (v1 -> v2)
24− -> M.Map k1 v1 -> M.Map k2 v2
25−transformMap fk fv = M.fromAscList . map (fk *** fv) . M.toAscList
26−{-# INLINE transformMap #-}
27−
28−-- | Transform a 'H.HashMap' into a 'M.Map'.
29−hashMap :: (Ord k2) => (k1 -> k2) -> (v1 -> v2)
30− -> H.HashMap k1 v1 -> M.Map k2 v2
31−hashMap fk kv = M.fromList . map (fk *** kv) . H.toList
32−{-# INLINE hashMap #-}
33−
34−-- | Transform a 'M.Map' into a 'H.HashMap'.
35−mapHash :: (Eq k2, Hashable k2) => (k1 -> k2) -> M.Map k1 v -> H.HashMap k2 v
36−mapHash fk = H.fromList . map (first fk) . M.toList
37−{-# INLINE mapHash #-}
20+-- | Transform a 'M.Map' into a 'H.HashMap' while transforming the keys.
21+mapHashKeyVal :: (Eq k2, Hashable k2) => (k1 -> k2) -> (v1 -> v2)
22+ -> M.Map k1 v1 -> H.HashMap k2 v2
23+mapHashKeyVal fk kv = M.foldrWithKey (\k v -> H.insert (fk k) (kv v)) H.empty
24+{-# INLINE mapHashKeyVal #-}
25+
26+-- | Transform a 'M.Map' into a 'H.HashMap' while transforming the keys.
27+hashMapKey :: (Ord k2) => (k1 -> k2)
28+ -> H.HashMap k1 v -> M.Map k2 v
29+hashMapKey kv = H.foldrWithKey (M.insert . kv) M.empty
30+{-# INLINE hashMapKey #-}
31+
32+-- | Transform the keys and values of a 'H.HashMap'.
33+mapKeyVal :: (Eq k2, Hashable k2) => (k1 -> k2) -> (v1 -> v2)
34+ -> H.HashMap k1 v1 -> H.HashMap k2 v2
35+mapKeyVal fk kv = H.foldrWithKey (\k v -> H.insert (fk k) (kv v)) H.empty
36+{-# INLINE mapKeyVal #-}
37+
38+-- | Transform the keys of a 'H.HashMap'.
39+mapKey :: (Eq k2, Hashable k2) => (k1 -> k2) -> H.HashMap k1 v -> H.HashMap k2 v
40+mapKey fk = mapKeyVal fk id
41+{-# INLINE mapKey #-}
3842
3943strict :: L.ByteString -> Text
4044strict = decode . B.concat . L.toChunks
Data/Aeson/Generic.hs+311−7
@@ -1,11 +1,11 @@
1−{-# LANGUAGE PatternGuards, RankNTypes, ScopedTypeVariables #-}
1+{-# LANGUAGE PatternGuards, Rank2Types, ScopedTypeVariables #-}
22
33-- |
44-- Module: Data.Aeson.Generic
55-- Copyright: (c) 2011 MailRank, Inc.
66-- (c) 2008, 2009 Lennart Augustsson
77-- License: BSD3
8−-- Maintainer: Bryan O'Sullivan <bos@mailrank.com>
8+-- Maintainer: Bryan O'Sullivan <bos@serpentine.com>
99-- Stability: experimental
1010-- Portability: portable
1111--
@@ -20,11 +20,315 @@ module Data.Aeson.Generic
2020 , toJSON
2121 ) where
2222
23−import Data.Aeson.Types (Value, Result, genericFromJSON, genericToJSON)
24−import Data.Data (Data)
23+import Control.Applicative ((<$>))
24+import Control.Arrow (first)
25+import Control.Monad.State.Strict
26+import Data.Aeson.Functions
27+import Data.Aeson.Types hiding (FromJSON(..), ToJSON(..), fromJSON)
28+import Data.Attoparsec.Number (Number)
29+import Data.Generics
30+import Data.Hashable (Hashable)
31+import Data.Int (Int8, Int16, Int32, Int64)
32+import Data.IntSet (IntSet)
33+import Data.Maybe (fromJust)
34+import Data.Text (Text, pack, unpack)
35+import Data.Text.Encoding (encodeUtf8)
36+import Data.Time.Clock (UTCTime)
37+import Data.Word (Word, Word8, Word16, Word32, Word64)
38+import qualified Data.Aeson.Types as T
39+import qualified Data.ByteString as B
40+import qualified Data.ByteString.Lazy as L
41+import qualified Data.HashMap.Strict as H
42+import qualified Data.Map as Map
43+import qualified Data.Set as Set
44+import qualified Data.Text as DT
45+import qualified Data.Text.Lazy as LT
46+import qualified Data.Traversable as T
47+import qualified Data.Vector as V
2548
26−fromJSON :: (Data a) => Value -> Result a
27−fromJSON = genericFromJSON
49+type T a = a -> Value
2850
2951toJSON :: (Data a) => a -> Value
30−toJSON = genericToJSON
52+toJSON = toJSON_generic
53+ `ext1Q` list
54+ `ext1Q` vector
55+ `ext1Q` set
56+ `ext2Q'` mapAny
57+ `ext2Q'` hashMapAny
58+ -- Use the standard encoding for all base types.
59+ `extQ` (T.toJSON :: T Integer)
60+ `extQ` (T.toJSON :: T Int)
61+ `extQ` (T.toJSON :: T Int8)
62+ `extQ` (T.toJSON :: T Int16)
63+ `extQ` (T.toJSON :: T Int32)
64+ `extQ` (T.toJSON :: T Int64)
65+ `extQ` (T.toJSON :: T Word)
66+ `extQ` (T.toJSON :: T Word8)
67+ `extQ` (T.toJSON :: T Word16)
68+ `extQ` (T.toJSON :: T Word32)
69+ `extQ` (T.toJSON :: T Word64)
70+ `extQ` (T.toJSON :: T Double)
71+ `extQ` (T.toJSON :: T Number)
72+ `extQ` (T.toJSON :: T Float)
73+ `extQ` (T.toJSON :: T Rational)
74+ `extQ` (T.toJSON :: T Char)
75+ `extQ` (T.toJSON :: T Text)
76+ `extQ` (T.toJSON :: T LT.Text)
77+ `extQ` (T.toJSON :: T String)
78+ `extQ` (T.toJSON :: T B.ByteString)
79+ `extQ` (T.toJSON :: T L.ByteString)
80+ `extQ` (T.toJSON :: T T.Value)
81+ `extQ` (T.toJSON :: T DotNetTime)
82+ `extQ` (T.toJSON :: T UTCTime)
83+ `extQ` (T.toJSON :: T IntSet)
84+ `extQ` (T.toJSON :: T Bool)
85+ `extQ` (T.toJSON :: T ())
86+ --`extQ` (T.toJSON :: T Ordering)
87+ where
88+ list xs = Array . V.fromList . map toJSON $ xs
89+ vector v = Array . V.map toJSON $ v
90+ set s = Array . V.fromList . map toJSON . Set.toList $ s
91+
92+ mapAny m
93+ | tyrep == typeOf DT.empty = remap id
94+ | tyrep == typeOf LT.empty = remap LT.toStrict
95+ | tyrep == typeOf "" = remap pack
96+ | tyrep == typeOf B.empty = remap decode
97+ | tyrep == typeOf L.empty = remap strict
98+ | otherwise = modError "toJSON" $
99+ "cannot convert map keyed by type " ++ show tyrep
100+ where tyrep = typeOf . head . Map.keys $ m
101+ remap f = Object . mapHashKeyVal (f . fromJust . cast) toJSON $ m
102+
103+ hashMapAny m
104+ | tyrep == typeOf DT.empty = remap id
105+ | tyrep == typeOf LT.empty = remap LT.toStrict
106+ | tyrep == typeOf "" = remap pack
107+ | tyrep == typeOf B.empty = remap decode
108+ | tyrep == typeOf L.empty = remap strict
109+ | otherwise = modError "toJSON" $
110+ "cannot convert map keyed by type " ++ show tyrep
111+ where tyrep = typeOf . head . H.keys $ m
112+ remap f = Object . mapKeyVal (f . fromJust . cast) toJSON $ m
113+
114+-- Skip leading '_' in field name so we can use keywords
115+-- etc. as field names.
116+mungeField :: String -> Text
117+mungeField ('_':cs) = pack cs
118+mungeField cs = pack cs
119+
120+toJSON_generic :: (Data a) => a -> Value
121+toJSON_generic = generic
122+ where
123+ -- Generic encoding of an algebraic data type.
124+ generic a =
125+ case dataTypeRep (dataTypeOf a) of
126+ -- No constructor, so it must be an error value. Code
127+ -- it anyway as Null.
128+ AlgRep [] -> Null
129+ -- Elide a single constructor and just code the arguments.
130+ AlgRep [c] -> encodeArgs c (gmapQ toJSON a)
131+ -- For multiple constructors, make an object with a
132+ -- field name that is the constructor (except lower
133+ -- case) and the data is the arguments encoded.
134+ AlgRep _ -> encodeConstr (toConstr a) (gmapQ toJSON a)
135+ rep -> err (dataTypeOf a) rep
136+ where
137+ err dt r = modError "toJSON" $ "not AlgRep " ++
138+ show r ++ "(" ++ show dt ++ ")"
139+ -- Encode nullary constructor as a string.
140+ -- Encode non-nullary constructors as an object with the constructor
141+ -- name as the single field and the arguments as the value.
142+ -- Use an array if the are no field names, but elide singleton arrays,
143+ -- and use an object if there are field names.
144+ encodeConstr c [] = String . constrString $ c
145+ encodeConstr c as = object [(constrString c, encodeArgs c as)]
146+
147+ constrString = pack . showConstr
148+
149+ encodeArgs c = encodeArgs' (constrFields c)
150+ encodeArgs' [] [j] = j
151+ encodeArgs' [] js = Array . V.fromList $ js
152+ encodeArgs' ns js = object $ zip (map mungeField ns) js
153+
154+
155+fromJSON :: (Data a) => Value -> Result a
156+fromJSON = parse parseJSON
157+
158+type F a = Parser a
159+
160+parseJSON :: (Data a) => Value -> Parser a
161+parseJSON j = parseJSON_generic j
162+ `ext1R` list
163+ `ext1R` vector
164+ `ext2R'` mapAny
165+ `ext2R'` hashMapAny
166+ -- Use the standard encoding for all base types.
167+ `extR` (value :: F Integer)
168+ `extR` (value :: F Int)
169+ `extR` (value :: F Int8)
170+ `extR` (value :: F Int16)
171+ `extR` (value :: F Int32)
172+ `extR` (value :: F Int64)
173+ `extR` (value :: F Word)
174+ `extR` (value :: F Word8)
175+ `extR` (value :: F Word16)
176+ `extR` (value :: F Word32)
177+ `extR` (value :: F Word64)
178+ `extR` (value :: F Double)
179+ `extR` (value :: F Number)
180+ `extR` (value :: F Float)
181+ `extR` (value :: F Rational)
182+ `extR` (value :: F Char)
183+ `extR` (value :: F Text)
184+ `extR` (value :: F LT.Text)
185+ `extR` (value :: F String)
186+ `extR` (value :: F B.ByteString)
187+ `extR` (value :: F L.ByteString)
188+ `extR` (value :: F T.Value)
189+ `extR` (value :: F DotNetTime)
190+ `extR` (value :: F UTCTime)
191+ `extR` (value :: F IntSet)
192+ `extR` (value :: F Bool)
193+ `extR` (value :: F ())
194+ where
195+ value :: (T.FromJSON a) => Parser a
196+ value = T.parseJSON j
197+ list :: (Data a) => Parser [a]
198+ list = V.toList <$> parseJSON j
199+ vector :: (Data a) => Parser (V.Vector a)
200+ vector = case j of
201+ Array js -> V.mapM parseJSON js
202+ _ -> myFail
203+
204+ mapAny :: forall e f. (Data e, Data f) => Parser (Map.Map f e)
205+ mapAny
206+ | tyrep == typeOf DT.empty = process id
207+ | tyrep == typeOf LT.empty = process LT.fromStrict
208+ | tyrep == typeOf "" = process DT.unpack
209+ | tyrep == typeOf B.empty = process encodeUtf8
210+ | tyrep == typeOf L.empty = process lazy
211+ | otherwise = myFail
212+ where
213+ process f = maybe myFail return . cast =<< parseWith f
214+ parseWith :: (Ord c) => (Text -> c) -> Parser (Map.Map c e)
215+ parseWith f = case j of
216+ Object js -> Map.fromList . map (first f) . H.toList <$>
217+ T.mapM parseJSON js
218+ _ -> myFail
219+ tyrep = typeOf (undefined :: f)
220+
221+ hashMapAny :: forall e f. (Data e, Data f) => Parser (H.HashMap f e)
222+ hashMapAny
223+ | tyrep == typeOf DT.empty = process id
224+ | tyrep == typeOf LT.empty = process LT.fromStrict
225+ | tyrep == typeOf "" = process DT.unpack
226+ | tyrep == typeOf B.empty = process encodeUtf8
227+ | tyrep == typeOf L.empty = process lazy
228+ | otherwise = myFail
229+ where
230+ process f = maybe myFail return . cast =<< parseWith f
231+ parseWith :: (Eq c, Hashable c) => (Text -> c) -> Parser (H.HashMap c e)
232+ parseWith f = case j of
233+ Object js -> mapKey f <$> T.mapM parseJSON js
234+ _ -> myFail
235+ tyrep = typeOf (undefined :: f)
236+
237+ myFail = modFail "parseJSON" $ "bad data: " ++ show j
238+
239+parseJSON_generic :: (Data a) => Value -> Parser a
240+parseJSON_generic j = generic
241+ where
242+ typ = dataTypeOf $ resType generic
243+ generic = case dataTypeRep typ of
244+ AlgRep [] -> case j of
245+ Null -> return (modError "parseJSON" "empty type")
246+ _ -> modFail "parseJSON" "no-constr bad data"
247+ AlgRep [_] -> decodeArgs (indexConstr typ 1) j
248+ AlgRep _ -> do (c, j') <- getConstr typ j; decodeArgs c j'
249+ rep -> modFail "parseJSON" $
250+ show rep ++ "(" ++ show typ ++ ")"
251+ getConstr t (Object o) | [(s, j')] <- fromJSObject o = do
252+ c <- readConstr' t s
253+ return (c, j')
254+ getConstr t (String js) = do c <- readConstr' t (unpack js)
255+ return (c, Null) -- handle nullary ctor
256+ getConstr _ _ = modFail "parseJSON" "bad constructor encoding"
257+ readConstr' t s =
258+ maybe (modFail "parseJSON" $ "unknown constructor: " ++ s ++ " " ++
259+ show t)
260+ return $ readConstr t s
261+
262+ decodeArgs c0 = go (numConstrArgs (resType generic) c0) c0
263+ (constrFields c0)
264+ where
265+ go 0 c _ Null = construct c [] -- nullary constructor
266+ go 1 c [] jd = construct c [jd] -- unary constructor
267+ go n c [] (Array js)
268+ | n > 1 = construct c (V.toList js) -- no field names
269+ -- FIXME? We could allow reading an array into a constructor
270+ -- with field names.
271+ go _ c fs@(_:_) (Object o) = selectFields o fs >>=
272+ construct c -- field names
273+ go _ c _ jd = modFail "parseJSON" $
274+ "bad decodeArgs data " ++ show (c, jd)
275+
276+ fromJSObject = map (first unpack) . H.toList
277+
278+ -- Build the value by stepping through the list of subparts.
279+ construct c = evalStateT $ fromConstrM f c
280+ where f :: (Data a) => StateT [Value] Parser a
281+ f = do js <- get
282+ case js of
283+ [] -> lift $ modFail "construct" "empty list"
284+ (j':js') -> do put js'; lift $ parseJSON j'
285+
286+ -- Select the named fields from a JSON object.
287+ selectFields fjs = mapM sel
288+ where sel f = maybe (modFail "parseJSON" $ "field does not exist " ++
289+ f) return $ H.lookup (mungeField f) fjs
290+
291+ -- Count how many arguments a constructor has. The value x is
292+ -- used to determine what type the constructor returns.
293+ numConstrArgs :: (Data a) => a -> Constr -> Int
294+ numConstrArgs x c = execState (fromConstrM f c `asTypeOf` return x) 0
295+ where f = do modify (+1); return undefined
296+
297+ resType :: MonadPlus m => m a -> a
298+ resType _ = modError "parseJSON" "resType"
299+
300+modFail :: (Monad m) => String -> String -> m a
301+modFail func err = fail $ "Data.Aeson.Generic." ++ func ++ ": " ++ err
302+
303+modError :: String -> String -> a
304+modError func err = error $ "Data.Aeson.Generic." ++ func ++ ": " ++ err
305+
306+
307+-- Type extension for binary type constructors.
308+
309+-- | Flexible type extension
310+ext2' :: (Data a, Typeable2 t)
311+ => c a
312+ -> (forall d1 d2. (Data d1, Data d2) => c (t d1 d2))
313+ -> c a
314+ext2' def ext = maybe def id (dataCast2 ext)
315+
316+-- | Type extension of queries for type constructors
317+ext2Q' :: (Data d, Typeable2 t)
318+ => (d -> q)
319+ -> (forall d1 d2. (Data d1, Data d2) => t d1 d2 -> q)
320+ -> d -> q
321+ext2Q' def ext = unQ ((Q def) `ext2'` (Q ext))
322+
323+-- | Type extension of readers for type constructors
324+ext2R' :: (Monad m, Data d, Typeable2 t)
325+ => m d
326+ -> (forall d1 d2. (Data d1, Data d2) => m (t d1 d2))
327+ -> m d
328+ext2R' def ext = unR ((R def) `ext2'` (R ext))
329+
330+-- | The type constructor for queries
331+newtype Q q x = Q { unQ :: x -> q }
332+
333+-- | The type constructor for readers
334+newtype R m x = R { unR :: m x }
Data/Aeson/Parser.hs+66−13
@@ -4,7 +4,7 @@
44-- Module: Data.Aeson.Parser
55-- Copyright: (c) 2011 MailRank, Inc.
66-- License: Apache
7−-- Maintainer: Bryan O'Sullivan <bos@mailrank.com>
7+-- Maintainer: Bryan O'Sullivan <bos@serpentine.com>
88-- Stability: experimental
99-- Portability: portable
1010--
@@ -14,7 +14,9 @@
1414module Data.Aeson.Parser
1515 (
1616 json
17+ , json'
1718 , value
19+ , value'
1820 , jstring
1921 ) where
2022
@@ -27,7 +29,6 @@ import Data.Attoparsec.Char8
2729import Data.Bits ((.|.), shiftL)
2830import Data.ByteString as B
2931import Data.Char (chr)
30−import Data.Map as Map
3132import Data.Monoid (mappend, mempty)
3233import Data.Text as T
3334import Data.Text.Encoding (decodeUtf8)
@@ -37,31 +38,63 @@ import qualified Data.Attoparsec as A
3738import qualified Data.Attoparsec.Zepto as Z
3839import qualified Data.ByteString.Char8 as B8
3940import qualified Data.ByteString.Unsafe as B
41+import qualified Data.HashMap.Strict as H
4042
4143-- | Parse a top-level JSON value. This must be either an object or
4244-- an array.
4345json :: Parser Value
44−json = do
45− c <- skipSpace *> satisfy (`B8.elem` "{[")
46− if c == '{'
47− then object_
48− else array_
46+json = json_ object_ array_
47+
48+-- | Parse a top-level JSON value. This must be either an object or
49+-- an array.
50+--
51+-- This is a strict parser version of 'json' which avoids
52+-- building up thunks during parsing. Prefer this version if most of
53+-- the JSON data needs to be accessed.
54+json' :: Parser Value
55+json' = json_ object_' array_'
56+
57+json_ :: Parser Value -> Parser Value -> Parser Value
58+json_ obj ary = do
59+ w <- skipSpace *> A.satisfy (\w -> w == 123 || w == 91)
60+ if w == 123
61+ then obj
62+ else ary
63+{-# INLINE json_ #-}
4964
5065object_ :: Parser Value
51−object_ = {-# SCC "object_" #-} do
66+object_ = {-# SCC "object_" #-} Object <$> objectValues value
67+
68+object_' :: Parser Value
69+object_' = {-# SCC "object_'" #-} do
70+ !vals <- objectValues value'
71+ return (Object vals)
72+
73+objectValues :: Parser Value -> Parser (H.HashMap Text Value)
74+objectValues val = do
5275 skipSpace
5376 let pair = do
5477 a <- jstring <* skipSpace
55− b <- char ':' *> skipSpace *> value
78+ b <- char ':' *> skipSpace *> val
5679 return (a,b)
5780 vals <- ((pair <* skipSpace) `sepBy` (char ',' *> skipSpace)) <* char '}'
58− return . Object $ Map.fromList vals
81+ return (H.fromList vals)
82+{-# INLINE objectValues #-}
5983
6084array_ :: Parser Value
61−array_ = {-# SCC "array_" #-} do
85+array_ = {-# SCC "array_" #-} Array <$> arrayValues value
86+
87+array_' :: Parser Value
88+array_' = {-# SCC "array_'" #-} do
89+ !vals <- arrayValues value'
90+ return (Array vals)
91+
92+arrayValues :: Parser Value -> Parser (Vector Value)
93+arrayValues val = do
6294 skipSpace
63− vals <- ((value <* skipSpace) `sepBy` (char ',' *> skipSpace)) <* char ']'
64− return . Array $ Vector.fromList vals
95+ vals <- ((val <* skipSpace) `sepBy` (char ',' *> skipSpace)) <* char ']'
96+ return (Vector.fromList vals)
97+{-# INLINE arrayValues #-}
6598
6699-- | Parse any JSON value. Use 'json' in preference to this function
67100-- if you are parsing data from an untrusted source.
@@ -79,6 +112,26 @@ value = most <|> (Number <$> number)
79112 'n' -> string "ull" *> pure Null
80113 _ -> error "attoparsec panic! the impossible happened!"
81114
115+-- | Strict version of 'value'. See also 'json''.
116+value' :: Parser Value
117+value' = most <|> num
118+ where
119+ most = do
120+ c <- satisfy (`B8.elem` "{[\"ftn")
121+ case c of
122+ '{' -> object_'
123+ '[' -> array_'
124+ '"' -> do
125+ !s <- jstring_
126+ return (String s)
127+ 'f' -> string "alse" *> pure (Bool False)
128+ 't' -> string "rue" *> pure (Bool True)
129+ 'n' -> string "ull" *> pure Null
130+ _ -> error "attoparsec panic! the impossible happened!"
131+ num = do
132+ !n <- number
133+ return (Number n)
134+
82135doubleQuote, backslash :: Word8
83136doubleQuote = 34
84137backslash = 92
Data/Aeson/TH.hs+285−130
@@ -38,8 +38,8 @@ import Control.Applicative
3838import Control.Monad
3939import Data.Aeson
4040import Data.Aeson.TH
41−import qualified Data.Map as M
42−import qualified Data.Text as T
41+import qualified Data.HashMap.Strict as H
42+import qualified Data.Text as T
4343import qualified Data.Vector as V
4444
4545instance 'ToJSON' a => 'ToJSON' (D a) where
@@ -47,21 +47,23 @@ instance 'ToJSON' a => 'ToJSON' (D a) where
4747 \value ->
4848 case value of
4949 Nullary ->
50− 'object' ['T.pack' \"Nullary\" .= 'toJSON' ([] :: [()])]
50+ 'object' [T.pack \"Nullary\" .= 'toJSON' ([] :: [()])]
5151 Unary arg1 ->
52− 'object' ['T.pack' \"Unary\" .= 'toJSON' arg1]
52+ 'object' [T.pack \"Unary\" .= 'toJSON' arg1]
5353 Product arg1 arg2 arg3 ->
54− 'object' [ 'T.pack' \"Product\"
55− .= 'toJSON' [ 'toJSON' arg1
56− , 'toJSON' arg2
57− , 'toJSON' arg3
58− ]
54+ 'object' [ T.pack \"Product\"
55+ .= ('Array' $ 'V.create' $ do
56+ mv <- 'VM.unsafeNew' 3
57+ 'VM.unsafeWrite' mv 0 ('toJSON' arg1)
58+ 'VM.unsafeWrite' mv 1 ('toJSON' arg2)
59+ 'VM.unsafeWrite' mv 2 ('toJSON' arg3)
60+ return mv)
5961 ]
6062 Record arg1 arg2 arg3 ->
61− 'object' [ 'T.pack' \"Record\"
62− .= 'object' [ 'T.pack' \"One\" '.=' arg1
63− , 'T.pack' \"Two\" '.=' arg2
64− , 'T.pack' \"Three\" '.=' arg3
63+ 'object' [ T.pack \"Record\"
64+ .= 'object' [ T.pack \"One\" '.=' arg1
65+ , T.pack \"Two\" '.=' arg2
66+ , T.pack \"Three\" '.=' arg3
6567 ]
6668 ]
6769@
@@ -72,35 +74,45 @@ instance 'FromJSON' a => 'FromJSON' (D a) where
7274 \value ->
7375 case value of
7476 'Object' obj ->
75− case 'M.toList' obj of
77+ case H.toList obj of
7678 [(conKey, conVal)] ->
77− case conKey of
78− _ | (conKey '==' 'T.pack' \"Nullary\") ->
79− case conVal of
80− 'Array' arr | 'V.null' arr -> 'pure' Nullary
81− _ -> 'mzero'
82− | (conKey '==' 'T.pack' \"Unary\") ->
83− case conVal of
84− arg -> Unary '<$>' 'parseJSON' arg
85− | (conKey '==' 'T.pack' \"Product\") ->
86− case conVal of
87− 'Array' arr | 'V.length' arr '==' 3 ->
88− 'Product' '<$>' 'parseJSON' (arr 'V.!' 0)
89− '<*>' 'parseJSON' (arr 'V.!' 1)
90− '<*>' 'parseJSON' (arr 'V.!' 2)
91− _ -> 'mzero'
92− | (conKey '==' 'T.pack' \"Record\") ->
93− case conVal of
94− 'Object' obj ->
95− Record '<$>' (obj '.:' 'T.pack' \"One\")
96− '<*>' (obj '.:' 'T.pack' \"Two\")
97− '<*>' (obj '.:' 'T.pack' \"Three\")
98− _ -> 'mzero'
99− | 'otherwise' -> 'mzero'
100− _ -> 'mzero'
101− _ -> 'mzero'
79+ case conKey of
80+ _ | conKey == T.pack \"Nullary\" ->
81+ case conVal of
82+ 'Array' arr ->
83+ if V.null arr
84+ then pure Nullary
85+ else fail \"\<error message\>\"
86+ _ -> fail \"\<error message\>\"
87+ | conKey == T.pack \"Unary\" ->
88+ case conVal of
89+ arg -> Unary \<$\> parseJSON arg
90+ | conKey == T.pack \"Product\" ->
91+ case conVal of
92+ 'Array' arr ->
93+ if V.length arr == 3
94+ then Product \<$\> 'parseJSON' (arr `V.unsafeIndex` 0)
95+ \<*\> 'parseJSON' (arr `V.unsafeIndex` 1)
96+ \<*\> 'parseJSON' (arr `V.unsafeIndex` 2)
97+ else fail \"\<error message\>\"
98+ _ -> fail \"\<error message\>\"
99+ | conKey == T.pack \"Record\" ->
100+ case conVal of
101+ 'Object' recObj ->
102+ if H.size recObj == 3
103+ then Record \<$\> recObj '.:' T.pack \"One\"
104+ \<*\> recObj '.:' T.pack \"Two\"
105+ \<*\> recObj '.:' T.pack \"Three\"
106+ else fail \"\<error message\>\"
107+ _ -> fail \"\<error message\>\"
108+ | otherwise -> fail \"\<error message\>\"
109+ _ -> fail \"\<error message\>\"
110+ _ -> fail \"\<error message\>\"
102111@
103112
113+Note that every \"\<error message\>\" is in fact a descriptive message which
114+provides as much information as is reasonable about the failed parse.
115+
104116Now we can use the newly created instances.
105117
106118@
@@ -114,6 +126,13 @@ d = Record { testOne = 3.14159
114126>>> fromJSON (toJSON d) == Success d
115127> True
116128
129+Please note that you can derive instances for tuples using the following syntax:
130+
131+@
132+-- FromJSON and ToJSON instances for 4-tuples.
133+$('deriveJSON' id ''(,,,))
134+@
135+
117136-}
118137
119138module Data.Aeson.TH
@@ -131,34 +150,38 @@ module Data.Aeson.TH
131150--------------------------------------------------------------------------------
132151
133152-- from aeson:
134−import Data.Aeson ( toJSON, object, (.=), (.:)
153+import Data.Aeson ( toJSON, Object, object, (.=)
135154 , ToJSON, toJSON
136155 , FromJSON, parseJSON
137156 )
138−import Data.Aeson.Types ( Value(..) )
157+import Data.Aeson.Types ( Value(..), Parser )
139158-- from base:
140159import Control.Applicative ( pure, (<$>), (<*>) )
141−import Control.Monad ( return, mapM, mzero, liftM2 )
160+import Control.Monad ( return, mapM, liftM2, fail )
142161import Data.Bool ( otherwise )
143162import Data.Eq ( (==) )
144163import Data.Function ( ($), (.), id )
145164import Data.Functor ( fmap )
146−import Data.List ( (++), foldl', map, zip, genericLength )
147−import Prelude ( String, (-), Integer, error )
165+import Data.List ( (++), foldl, foldl', intercalate
166+ , length, map, zip, genericLength
167+ )
168+import Data.Maybe ( Maybe(Nothing, Just) )
169+import Prelude ( String, (-), Integer, fromIntegral, error )
170+import Text.Printf ( printf )
148171import Text.Show ( show )
149172#if __GLASGOW_HASKELL__ < 700
150−import Control.Monad ( (>>=), fail )
173+import Control.Monad ( (>>=) )
151174import Prelude ( fromInteger )
152175#endif
153−-- from containers:
154−import qualified Data.Map as M ( toList )
176+-- from unordered-containers:
177+import qualified Data.HashMap.Strict as H ( lookup, toList, size )
155178-- from template-haskell:
156179import Language.Haskell.TH
157180-- from text:
158−import qualified Data.Text as T ( pack )
181+import qualified Data.Text as T ( Text, pack, unpack )
159182-- from vector:
160−import qualified Data.Vector as V ( (!), null, length )
161−
183+import qualified Data.Vector as V ( unsafeIndex, null, length, create )
184+import qualified Data.Vector.Mutable as VM ( unsafeNew, unsafeWrite )
162185
163186
164187--------------------------------------------------------------------------------
@@ -210,7 +233,11 @@ The above (ToJSON a) constraint is not necessary and perhaps undesirable.
210233-- instance 'ToJSON' Foo where
211234-- 'toJSON' =
212235-- \value -> case value of
213−-- Foo arg1 arg2 -> 'toJSON' ['toJSON' arg1, 'toJSON' arg2]
236+-- Foo arg1 arg2 -> 'Array' $ 'V.create' $ do
237+-- mv <- 'VM.unsafeNew' 2
238+-- 'VM.unsafeWrite' mv 0 ('toJSON' arg1)
239+-- 'VM.unsafeWrite' mv 1 ('toJSON' arg2)
240+-- return mv
214241-- @
215242deriveToJSON :: (String -> String)
216243 -- ^ Function to change field names.
@@ -241,12 +268,12 @@ deriveToJSON withField name =
241268-- Example:
242269--
243270-- @
244−-- data Foo = Foo 'Int'
271+-- data Foo = Foo Int
245272-- @
246273--
247274-- @
248275-- encodeFoo :: Foo -> 'Value'
249−-- encodeFoo = $('mkToJSON' 'id' ''Foo)
276+-- encodeFoo = $('mkToJSON' id ''Foo)
250277-- @
251278--
252279-- This will splice in the following code:
@@ -306,12 +333,28 @@ encodeArgs withExp _ (NormalC conName []) =
306333 []
307334-- Polyadic constructors with special case for unary constructors.
308335encodeArgs withExp _ (NormalC conName ts) = do
309− args <- mapM newName ["arg" ++ show n | (_, n) <- zip ts [1 :: Integer ..]]
310− let js = case [[e|toJSON|] `appE` varE arg | arg <- args] of
311− -- Single argument is directly converted.
312− [e] -> e
313− -- Multiple arguments are converted to a JSON array.
314− es -> [e|toJSON|] `appE` listE es
336+ let len = length ts
337+ args <- mapM newName ["arg" ++ show n | n <- [1..len]]
338+ js <- case [[e|toJSON|] `appE` varE arg | arg <- args] of
339+ -- Single argument is directly converted.
340+ [e] -> return e
341+ -- Multiple arguments are converted to a JSON array.
342+ es -> do
343+ mv <- newName "mv"
344+ let newMV = bindS (varP mv)
345+ ([e|VM.unsafeNew|] `appE`
346+ litE (integerL $ fromIntegral len))
347+ stmts = [ noBindS $
348+ [e|VM.unsafeWrite|] `appE`
349+ (varE mv) `appE`
350+ litE (integerL ix) `appE`
351+ e
352+ | (ix, e) <- zip [(0::Integer)..] es
353+ ]
354+ ret = noBindS $ [e|return|] `appE` varE mv
355+ return $ [e|Array|] `appE`
356+ (varE 'V.create `appE`
357+ doE (newMV:stmts++[ret]))
315358 match (conP conName $ map varP args)
316359 (normalB $ withExp js)
317360 []
@@ -352,8 +395,8 @@ encodeArgs withExp withField (ForallC _ _ con) =
352395-- Example:
353396--
354397-- @
355−-- data Foo = Foo 'Char' 'Int'
356−-- $('deriveFromJSON' 'id' ''Foo)
398+-- data Foo = Foo Char Int
399+-- $('deriveFromJSON' id ''Foo)
357400-- @
358401--
359402-- This will splice in the following code:
@@ -362,10 +405,12 @@ encodeArgs withExp withField (ForallC _ _ con) =
362405-- instance 'FromJSON' Foo where
363406-- 'parseJSON' =
364407-- \value -> case value of
365−-- 'Array' arr | ('V.length' arr '==' 2) ->
366−-- Foo '<$>' 'parseJSON' (arr 'V.!' 0)
367−-- '<*>' 'parseJSON' (arr 'V.!' 1)
368−-- _ -> 'mzero'
408+-- 'Array' arr ->
409+-- if (V.length arr == 2)
410+-- then Foo \<$\> 'parseJSON' (arr `V.unsafeIndex` 0)
411+-- \<*\> 'parseJSON' (arr `V.unsafeIndex` 1)
412+-- else fail \"\<error message\>\"
413+-- other -> fail \"\<error message\>\"
369414-- @
370415deriveFromJSON :: (String -> String)
371416 -- ^ Function to change field names.
@@ -382,7 +427,7 @@ deriveFromJSON withField name =
382427 (classType `appT` instanceType)
383428 [ funD 'parseJSON
384429 [ clause []
385− (normalB $ consFromJSON withField cons)
430+ (normalB $ consFromJSON name withField cons)
386431 []
387432 ]
388433 ]
@@ -402,36 +447,38 @@ deriveFromJSON withField name =
402447--
403448-- @
404449-- parseFoo :: 'Value' -> 'Parser' Foo
405−-- parseFoo = $('mkParseJSON' 'id' ''Foo)
450+-- parseFoo = $('mkParseJSON' id ''Foo)
406451-- @
407452--
408453-- This will splice in the following code:
409454--
410455-- @
411−-- \\value -> case value of arg -> Foo '<$>' 'parseJSON' arg
456+-- \\value -> case value of arg -> Foo \<$\> 'parseJSON' arg
412457-- @
413458mkParseJSON :: (String -> String) -- ^ Function to change field names.
414459 -> Name -- ^ Name of the encoded type.
415460 -> Q Exp
416461mkParseJSON withField name =
417− withType name (\_ cons -> consFromJSON withField cons)
462+ withType name (\_ cons -> consFromJSON name withField cons)
418463
419464-- | Helper function used by both 'deriveFromJSON' and 'mkParseJSON'. Generates
420465-- code to parse the JSON encoding of a number of constructors. All constructors
421466-- must be from the same type.
422−consFromJSON :: (String -> String)
467+consFromJSON :: Name
468+ -- ^ Name of the type to which the constructors belong.
469+ -> (String -> String)
423470 -- ^ Function to change field names.
424471 -> [Con]
425472 -- ^ Constructors for which to generate JSON parsing code.
426473 -> Q Exp
427−consFromJSON _ [] = error $ "Data.Aeson.TH.consFromJSON: "
428− ++ "Not a single constructor given!"
429−consFromJSON withField [con] = do
474+consFromJSON _ _ [] = error $ "Data.Aeson.TH.consFromJSON: "
475+ ++ "Not a single constructor given!"
476+consFromJSON tName withField [con] = do
430477 value <- newName "value"
431478 lam1E (varP value)
432479 $ caseE (varE value)
433− (parseArgs withField con)
434−consFromJSON withField cons = do
480+ (parseArgs tName withField con)
481+consFromJSON tName withField cons = do
435482 value <- newName "value"
436483 obj <- newName "obj"
437484 conKey <- newName "conKey"
@@ -440,12 +487,19 @@ consFromJSON withField cons = do
440487 let -- Convert the Data.Map inside the Object to a list and pattern match
441488 -- against it. It must contain a single element otherwise the parse will
442489 -- fail.
443− caseLst = caseE ([e|M.toList|] `appE` varE obj)
490+ caseLst = caseE ([e|H.toList|] `appE` varE obj)
444491 [ match (listP [tupP [varP conKey, varP conVal]])
445492 (normalB caseKey)
446493 []
447− , errorMatch
494+ , do other <- newName "other"
495+ match (varP other)
496+ (normalB $ [|wrongPairCountFail|]
497+ `appE` (litE $ stringL $ show tName)
498+ `appE` ([|show . length|] `appE` varE other)
499+ )
500+ []
448501 ]
502+
449503 caseKey = caseE (varE conKey)
450504 [match wildP (guardedB guards) []]
451505 guards = [ do g <- normalG $ infixApp (varE conKey)
@@ -454,44 +508,59 @@ consFromJSON withField cons = do
454508 `appE` conNameExp con
455509 )
456510 e <- caseE (varE conVal)
457− (parseArgs withField con)
511+ (parseArgs tName withField con)
458512 return (g, e)
459513 | con <- cons
460514 ]
461515 ++
462− [liftM2 (,) (normalG [e|otherwise|]) [e|mzero|]]
516+ [ liftM2 (,)
517+ (normalG [e|otherwise|])
518+ ( [|conNotFoundFail|]
519+ `appE` (litE $ stringL $ show tName)
520+ `appE` listE (map (litE . stringL . nameBase . getConName) cons)
521+ `appE` ([|T.unpack|] `appE` varE conKey)
522+ )
523+ ]
463524
464525 lam1E (varP value)
465526 $ caseE (varE value)
466527 [ match (conP 'Object [varP obj])
467528 (normalB caseLst)
468529 []
469− , errorMatch
530+ , do other <- newName "other"
531+ match (varP other)
532+ ( normalB
533+ $ [|noObjectFail|]
534+ `appE` (litE $ stringL $ show tName)
535+ `appE` ([|valueConName|] `appE` varE other)
536+ )
537+ []
470538 ]
471− where
472− -- Makes a string literal expression from a constructor's name.
473− conNameExp :: Con -> Q Exp
474− conNameExp = litE . stringL . nameBase . getConName
475539
476−-- | Generates code to parse the JSON encoding of a single
477−-- constructor.
478−parseArgs :: (String -> String) -- ^ Function to change field names.
540+-- | Generates code to parse the JSON encoding of a single constructor.
541+parseArgs :: Name -- ^ Name of the type to which the constructor belongs.
542+ -> (String -> String) -- ^ Function to change field names.
479543 -> Con -- ^ Constructor for which to generate JSON parsing code.
480544 -> [Q Match]
481545-- Nullary constructors.
482−parseArgs _ (NormalC conName []) =
546+parseArgs tName _ (NormalC conName []) =
483547 [ do arr <- newName "arr"
484− g <- normalG $ [|V.null|] `appE` varE arr
485− e <- [e|pure|] `appE` conE conName
486− -- TODO: Use applicative style: guardedB [(,) <$> g' <*> e']
487− -- But first need to have "instance Applicative Q".
488548 match (conP 'Array [varP arr])
489− (guardedB [return (g, e)])
549+ ( normalB $ condE ([|V.null|] `appE` varE arr)
550+ ([e|pure|] `appE` conE conName)
551+ ( parseTypeMismatch tName conName
552+ (litE $ stringL "an empty Array")
553+ ( infixApp (litE $ stringL $ "Array of length ")
554+ [|(++)|]
555+ ([|show . V.length|] `appE` varE arr)
556+ )
557+ )
558+ )
490559 []
491− , errorMatch
560+ , matchFailed tName conName "Array"
492561 ]
493562-- Unary constructors.
494−parseArgs _ (NormalC conName [_]) =
563+parseArgs _ _ (NormalC conName [_]) =
495564 [ do arg <- newName "arg"
496565 match (varP arg)
497566 ( normalB $ infixApp (conE conName)
@@ -500,69 +569,142 @@ parseArgs _ (NormalC conName [_]) =
500569 )
501570 []
502571 ]
503−
504572-- Polyadic constructors.
505−parseArgs _ (NormalC conName ts) = parseProduct conName $ genericLength ts
573+parseArgs tName _ (NormalC conName ts) = parseProduct tName conName $ genericLength ts
506574-- Records.
507−parseArgs withField (RecC conName ts) =
508− [ do obj <- newName "obj"
509− -- List of: "obj .: "<FIELD>""
510− let x:xs = [ infixApp (varE obj)
511− [|(.:)|]
512− ( [e|T.pack|]
513− `appE`
514− fieldNameExp withField field
515− )
575+parseArgs tName withField (RecC conName ts) =
576+ [ do obj <- newName "recObj"
577+ let x:xs = [ [|lookupField|]
578+ `appE` (litE $ stringL $ show tName)
579+ `appE` (litE $ stringL $ nameBase conName)
580+ `appE` (varE obj)
581+ `appE` ( [e|T.pack|]
582+ `appE`
583+ fieldNameExp withField field
584+ )
516585 | (field, _, _) <- ts
517586 ]
518587 match (conP 'Object [varP obj])
519− ( normalB $ foldl' (\a b -> infixApp a [|(<*>)|] b)
520− (infixApp (conE conName) [|(<$>)|] x)
521− xs
588+ ( normalB $ condE ( infixApp ([|H.size|] `appE` varE obj)
589+ [|(==)|]
590+ (litE $ integerL $ genericLength ts)
591+ )
592+ ( foldl' (\a b -> infixApp a [|(<*>)|] b)
593+ (infixApp (conE conName) [|(<$>)|] x)
594+ xs
595+ )
596+ ( parseTypeMismatch tName conName
597+ ( litE $ stringL $ "Object with "
598+ ++ show (length ts)
599+ ++ " name/value pairs"
600+ )
601+ ( infixApp ([|show . H.size|] `appE` varE obj)
602+ [|(++)|]
603+ (litE $ stringL $ " name/value pairs")
604+ )
605+ )
522606 )
523607 []
524− , errorMatch
608+ , matchFailed tName conName "Object"
525609 ]
526610-- Infix constructors. Apart from syntax these are the same as
527611-- polyadic constructors.
528−parseArgs _ (InfixC _ conName _) = parseProduct conName 2
612+parseArgs tName _ (InfixC _ conName _) = parseProduct tName conName 2
529613-- Existentially quantified constructors. We ignore the quantifiers
530614-- and proceed with the contained constructor.
531−parseArgs withField (ForallC _ _ con) = parseArgs withField con
615+parseArgs tName withField (ForallC _ _ con) = parseArgs tName withField con
532616
533617-- | Generates code to parse the JSON encoding of an n-ary
534618-- constructor.
535−parseProduct :: Name -- ^ 'Con'structor name.
619+parseProduct :: Name -- ^ Name of the type to which the constructor belongs.
620+ -> Name -- ^ 'Con'structor name.
536621 -> Integer -- ^ 'Con'structor arity.
537622 -> [Q Match]
538−parseProduct conName numArgs =
623+parseProduct tName conName numArgs =
539624 [ do arr <- newName "arr"
540− g <- normalG $ infixApp ([|V.length|] `appE` varE arr)
541− [|(==)|]
542− (litE $ integerL numArgs)
543− -- List of: "parseJSON (arr V.! <IX>)"
625+ -- List of: "parseJSON (arr `V.unsafeIndex` <IX>)"
544626 let x:xs = [ [|parseJSON|]
545627 `appE`
546628 infixApp (varE arr)
547− [|(V.!)|]
629+ [|V.unsafeIndex|]
548630 (litE $ integerL ix)
549631 | ix <- [0 .. numArgs - 1]
550632 ]
551− e <- foldl' (\a b -> infixApp a [|(<*>)|] b)
552− (infixApp (conE conName) [|(<$>)|] x)
553− xs
554633 match (conP 'Array [varP arr])
555− (guardedB [return (g, e)])
634+ (normalB $ condE ( infixApp ([|V.length|] `appE` varE arr)
635+ [|(==)|]
636+ (litE $ integerL numArgs)
637+ )
638+ ( foldl' (\a b -> infixApp a [|(<*>)|] b)
639+ (infixApp (conE conName) [|(<$>)|] x)
640+ xs
641+ )
642+ ( parseTypeMismatch tName conName
643+ (litE $ stringL $ "Array of length " ++ show numArgs)
644+ ( infixApp (litE $ stringL $ "Array of length ")
645+ [|(++)|]
646+ ([|show . V.length|] `appE` varE arr)
647+ )
648+ )
649+ )
556650 []
557− , errorMatch
651+ , matchFailed tName conName "Array"
558652 ]
559653
560−-- |
561−-- @
562−-- _ -> 'mzero'
563−-- @
564−errorMatch :: Q Match
565−errorMatch = match wildP (normalB [|mzero|]) []
654+
655+--------------------------------------------------------------------------------
656+-- Parsing errors
657+--------------------------------------------------------------------------------
658+
659+matchFailed :: Name -> Name -> String -> MatchQ
660+matchFailed tName conName expected = do
661+ other <- newName "other"
662+ match (varP other)
663+ ( normalB $ parseTypeMismatch tName conName
664+ (litE $ stringL expected)
665+ ([|valueConName|] `appE` varE other)
666+ )
667+ []
668+
669+parseTypeMismatch :: Name -> Name -> ExpQ -> ExpQ -> ExpQ
670+parseTypeMismatch tName conName expected actual =
671+ foldl appE
672+ [|parseTypeMismatch'|]
673+ [ litE $ stringL $ nameBase conName
674+ , litE $ stringL $ show tName
675+ , expected
676+ , actual
677+ ]
678+
679+lookupField :: (FromJSON a) => String -> String -> Object -> T.Text -> Parser a
680+lookupField tName rec obj key =
681+ case H.lookup key obj of
682+ Nothing -> unknownFieldFail tName rec (T.unpack key)
683+ Just v -> parseJSON v
684+
685+unknownFieldFail :: String -> String -> String -> Parser fail
686+unknownFieldFail tName rec key =
687+ fail $ printf "When parsing the record %s of type %s the key %s was not present."
688+ rec tName key
689+
690+noObjectFail :: String -> String -> Parser fail
691+noObjectFail t o =
692+ fail $ printf "When parsing %s expected Object but got %s." t o
693+
694+wrongPairCountFail :: String -> String -> Parser fail
695+wrongPairCountFail t n =
696+ fail $ printf "When parsing %s expected an Object with a single name/value pair but got %s pairs."
697+ t n
698+
699+conNotFoundFail :: String -> [String] -> String -> Parser fail
700+conNotFoundFail t cs o =
701+ fail $ printf "When parsing %s expected an Object with a name/value pair where the name is one of [%s], but got %s."
702+ t (intercalate ", " cs) o
703+
704+parseTypeMismatch' :: String -> String -> String -> String -> Parser fail
705+parseTypeMismatch' tName conName expected actual =
706+ fail $ printf "When parsing the constructor %s of type %s expected %s but got %s."
707+ conName tName expected actual
566708
567709
568710--------------------------------------------------------------------------------
@@ -604,8 +746,21 @@ tvbName :: TyVarBndr -> Name
604746tvbName (PlainTV name ) = name
605747tvbName (KindedTV name _) = name
606748
749+-- | Makes a string literal expression from a constructor's name.
750+conNameExp :: Con -> Q Exp
751+conNameExp = litE . stringL . nameBase . getConName
752+
607753-- | Creates a string literal expression from a record field name.
608754fieldNameExp :: (String -> String) -- ^ Function to change the field name.
609755 -> Name
610756 -> Q Exp
611757fieldNameExp f = litE . stringL . f . nameBase
758+
759+-- | The name of the outermost 'Value' constructor.
760+valueConName :: Value -> String
761+valueConName (Object _) = "Object"
762+valueConName (Array _) = "Array"
763+valueConName (String _) = "String"
764+valueConName (Number _) = "Number"
765+valueConName (Bool _) = "Boolean"
766+valueConName Null = "Null"
Data/Aeson/Types.hs+6−1085
@@ -1,18 +1,10 @@
1−{-# LANGUAGE DeriveDataTypeable, FlexibleInstances, GeneralizedNewtypeDeriving,
2− IncoherentInstances, OverlappingInstances, OverloadedStrings, Rank2Types,
3− ViewPatterns, FlexibleContexts, UndecidableInstances,
4− ScopedTypeVariables, PatternGuards #-}
5−
61{-# LANGUAGE CPP #-}
7−#ifdef DEFAULT_SIGNATURES
8−{-# LANGUAGE DefaultSignatures #-}
9−#endif
102
113-- |
124-- Module: Data.Aeson.Types
135-- Copyright: (c) 2011 MailRank, Inc.
146-- License: Apache
15−-- Maintainer: Bryan O'Sullivan <bos@mailrank.com>
7+-- Maintainer: Bryan O'Sullivan <bos@serpentine.com>
168-- Stability: experimental
179-- Portability: portable
1810--
@@ -43,1084 +35,13 @@ module Data.Aeson.Types
4335 , (.=)
4436 , (.:)
4537 , (.:?)
38+ , (.:/)
4639 , object
47− -- * Generic toJSON and fromJSON
48− , genericToJSON
49− , genericFromJSON
5040 ) where
5141
52−import Control.Applicative
53−import Control.Arrow (first)
54−import Control.Monad.State.Strict
55−import Control.DeepSeq (NFData(..))
56−import Data.Aeson.Functions
57−import Data.Attoparsec.Char8 (Number(..))
58−import Data.Generics
59−import Data.Hashable (Hashable(..))
60−import Data.Int (Int8, Int16, Int32, Int64)
61−import Data.IntSet (IntSet)
62−import Data.List (foldl')
63−import Data.Map (Map)
64−import Data.Maybe (fromJust)
65−import Data.Monoid (Dual(..), First(..), Last(..))
66−import Data.Monoid (Monoid(..))
67−import Data.Ratio (Ratio)
68−import Data.String (IsString(..))
69−import Data.Text (Text, pack, unpack)
70−import Data.Text.Encoding (encodeUtf8)
71−import Data.Time.Clock (UTCTime)
72−import Data.Time.Format (FormatTime, formatTime, parseTime)
73−import Data.Vector (Vector)
74−import Data.Word (Word, Word8, Word16, Word32, Word64)
75−import Foreign.Storable (Storable)
76−import System.Locale (defaultTimeLocale)
77−import qualified Data.ByteString as B
78−import qualified Data.ByteString.Lazy as LB
79−import qualified Data.HashMap.Strict as H
80−import qualified Data.HashSet as HashSet
81−import qualified Data.IntSet as IntSet
82−import qualified Data.Map as M
83−import qualified Data.Set as Set
84−import qualified Data.Text as T
85−import qualified Data.Text.Lazy as LT
86−import qualified Data.Traversable as T
87−import qualified Data.Vector as V
88−import qualified Data.Vector.Storable as VS
89−import qualified Data.Vector.Primitive as VP
90−import qualified Data.Vector.Unboxed as VU
91−import qualified Data.Vector.Generic as VG
92−
93−
94−-- | The result of running a 'Parser'.
95−data Result a = Error String
96− | Success a
97− deriving (Eq, Show, Typeable)
98−
99−instance (NFData a) => NFData (Result a) where
100− rnf (Success a) = rnf a
101− rnf (Error err) = rnf err
102−
103−instance Functor Result where
104− fmap f (Success a) = Success (f a)
105− fmap _ (Error err) = Error err
106− {-# INLINE fmap #-}
107−
108−instance Monad Result where
109− return = Success
110− {-# INLINE return #-}
111− Success a >>= k = k a
112− Error err >>= _ = Error err
113− {-# INLINE (>>=) #-}
114−
115−instance Applicative Result where
116− pure = return
117− {-# INLINE pure #-}
118− (<*>) = ap
119− {-# INLINE (<*>) #-}
120−
121−instance MonadPlus Result where
122− mzero = fail "mzero"
123− {-# INLINE mzero #-}
124− mplus a@(Success _) _ = a
125− mplus _ b = b
126− {-# INLINE mplus #-}
127−
128−instance Alternative Result where
129− empty = mzero
130− {-# INLINE empty #-}
131− (<|>) = mplus
132− {-# INLINE (<|>) #-}
133−
134−instance Monoid (Result a) where
135− mempty = fail "mempty"
136− {-# INLINE mempty #-}
137− mappend = mplus
138− {-# INLINE mappend #-}
139−
140−-- | Failure continuation.
141−type Failure f r = String -> f r
142−-- | Success continuation.
143−type Success a f r = a -> f r
144−
145−-- | A continuation-based parser type.
146−newtype Parser a = Parser {
147− runParser :: forall f r.
148− Failure f r
149− -> Success a f r
150− -> f r
151− }
152−
153−instance Monad Parser where
154− m >>= g = Parser $ \kf ks -> let ks' a = runParser (g a) kf ks
155− in runParser m kf ks'
156− {-# INLINE (>>=) #-}
157− return a = Parser $ \_kf ks -> ks a
158− {-# INLINE return #-}
159− fail msg = Parser $ \kf _ks -> kf msg
160− {-# INLINE fail #-}
161−
162−instance Functor Parser where
163− fmap f m = Parser $ \kf ks -> let ks' a = ks (f a)
164− in runParser m kf ks'
165− {-# INLINE fmap #-}
166−
167−instance Applicative Parser where
168− pure = return
169− {-# INLINE pure #-}
170− (<*>) = apP
171− {-# INLINE (<*>) #-}
172−
173−instance Alternative Parser where
174− empty = fail "empty"
175− {-# INLINE empty #-}
176− (<|>) = mplus
177− {-# INLINE (<|>) #-}
178−
179−instance MonadPlus Parser where
180− mzero = fail "mzero"
181− {-# INLINE mzero #-}
182− mplus a b = Parser $ \kf ks -> let kf' _ = runParser b kf ks
183− in runParser a kf' ks
184− {-# INLINE mplus #-}
185−
186−instance Monoid (Parser a) where
187− mempty = fail "mempty"
188− {-# INLINE mempty #-}
189− mappend = mplus
190− {-# INLINE mappend #-}
191−
192−apP :: Parser (a -> b) -> Parser a -> Parser b
193−apP d e = do
194− b <- d
195− a <- e
196− return (b a)
197−{-# INLINE apP #-}
198−
199−-- | A JSON \"object\" (key\/value map).
200−type Object = Map Text Value
201−
202−-- | A JSON \"array\" (sequence).
203−type Array = Vector Value
204−
205−-- | A JSON value represented as a Haskell value.
206−data Value = Object Object
207− | Array Array
208− | String Text
209− | Number Number
210− | Bool !Bool
211− | Null
212− deriving (Eq, Show, Typeable, Data)
213−
214−instance NFData Value where
215− rnf (Object o) = rnf o
216− rnf (Array a) = V.foldl' (\x y -> rnf y `seq` x) () a
217− rnf (String s) = rnf s
218− rnf (Number n) = case n of I i -> rnf i; D d -> rnf d
219− rnf (Bool b) = rnf b
220− rnf Null = ()
221−
222−instance IsString Value where
223− fromString = String . pack
224− {-# INLINE fromString #-}
225−
226−instance Hashable Value where
227− hash (Object o) = foldl' hashWithSalt 0 . M.toList $ o
228− hash (Array a) = V.foldl' hashWithSalt 1 a
229− hash (String s) = 2 `hashWithSalt` s
230− hash (Number n) = 3 `hashWithSalt` case n of I i -> hash i; D d -> hash d
231− hash (Bool b) = 4 `hashWithSalt` b
232− hash Null = 5
233−
234−-- | The empty array.
235−emptyArray :: Value
236−emptyArray = Array V.empty
237−
238−-- | The empty object.
239−emptyObject :: Value
240−emptyObject = Object M.empty
241−
242−-- | A key\/value pair for an 'Object'.
243−type Pair = (Text, Value)
244−
245−-- | Construct a 'Pair' from a key and a value.
246−(.=) :: ToJSON a => Text -> a -> Pair
247−name .= value = (name, toJSON value)
248−{-# INLINE (.=) #-}
249−
250−-- | Convert a value from JSON, failing if the types do not match.
251−fromJSON :: (FromJSON a) => Value -> Result a
252−fromJSON = parse parseJSON
253−{-# INLINE fromJSON #-}
254−
255−-- | Run a 'Parser'.
256−parse :: (a -> Parser b) -> a -> Result b
257−parse m v = runParser (m v) Error Success
258−{-# INLINE parse #-}
259−
260−-- | Run a 'Parser' with a 'Maybe' result type.
261−parseMaybe :: (a -> Parser b) -> a -> Maybe b
262−parseMaybe m v = runParser (m v) (const Nothing) Just
263−{-# INLINE parseMaybe #-}
264−
265−-- | Run a 'Parser' with an 'Either' result type.
266−parseEither :: (a -> Parser b) -> a -> Either String b
267−parseEither m v = runParser (m v) Left Right
268−{-# INLINE parseEither #-}
269−
270−-- | Retrieve the value associated with the given key of an 'Object'.
271−-- The result is 'empty' if the key is not present or the value cannot
272−-- be converted to the desired type.
273−--
274−-- This accessor is appropriate if the key and value /must/ be present
275−-- in an object for it to be valid. If the key and value are
276−-- optional, use '(.:?)' instead.
277−(.:) :: (FromJSON a) => Object -> Text -> Parser a
278−obj .: key = case M.lookup key obj of
279− Nothing -> fail $ "key " ++ show key ++ " not present"
280− Just v -> parseJSON v
281−{-# INLINE (.:) #-}
282−
283−-- | Retrieve the value associated with the given key of an 'Object'.
284−-- The result is 'Nothing' if the key is not present, or 'empty' if
285−-- the value cannot be converted to the desired type.
286−--
287−-- This accessor is most useful if the key and value can be absent
288−-- from an object without affecting its validity. If the key and
289−-- value are mandatory, use '(.:)' instead.
290−(.:?) :: (FromJSON a) => Object -> Text -> Parser (Maybe a)
291−obj .:? key = case M.lookup key obj of
292− Nothing -> pure Nothing
293− Just v -> parseJSON v
294−{-# INLINE (.:?) #-}
295−
296−-- | Create a 'Value' from a list of name\/value 'Pair's. If duplicate
297−-- keys arise, earlier keys and their associated values win.
298−object :: [Pair] -> Value
299−object = Object . M.fromList
300−{-# INLINE object #-}
301−
302−-- | A type that can be converted to JSON.
303−--
304−-- An example type and instance:
305−--
306−-- @data Coord { x :: Double, y :: Double }
307−--
308−-- instance ToJSON Coord where
309−-- toJSON (Coord x y) = 'object' [\"x\" '.=' x, \"y\" '.=' y]
310−-- @
311−--
312−-- This example assumes the OverloadedStrings language option is enabled.
313−class ToJSON a where
314− toJSON :: a -> Value
315−
316−#ifdef DEFAULT_SIGNATURES
317− default toJSON :: Data a => a -> Value
318− toJSON = genericToJSON
319−#endif
320−
321−-- | A type that can be converted from JSON, with the possibility of
322−-- failure.
323−--
324−-- When writing an instance, use 'mzero' or 'fail' to make a
325−-- conversion fail, e.g. if an 'Object' is missing a required key, or
326−-- the value is of the wrong type.
327−--
328−-- An example type and instance:
329−--
330−-- @data Coord { x :: Double, y :: Double }
331−--
332−-- instance FromJSON Coord where
333−-- parseJSON ('Object' v) = Coord '<$>'
334−-- v '.:' \"x\" '<*>'
335−-- v '.:' \"y\"
336−--
337−-- \-- A non-'Object' value is of the wrong type, so use 'mzero' to fail.
338−-- parseJSON _ = 'mzero'
339−-- @
340−--
341−-- This example assumes the OverloadedStrings language option is enabled.
342−class FromJSON a where
343− parseJSON :: Value -> Parser a
42+import Data.Aeson.Types.Class
43+import Data.Aeson.Types.Internal
34444
345−#ifdef DEFAULT_SIGNATURES
346− default parseJSON :: Data a => Value -> Parser a
347− parseJSON = genericParseJSON
45+#ifdef GENERICS
46+import Data.Aeson.Types.Generic ()
34847#endif
349−
350−instance (ToJSON a) => ToJSON (Maybe a) where
351− toJSON (Just a) = toJSON a
352− toJSON Nothing = Null
353− {-# INLINE toJSON #-}
354−
355−instance (FromJSON a) => FromJSON (Maybe a) where
356− parseJSON Null = pure Nothing
357− parseJSON a = Just <$> parseJSON a
358− {-# INLINE parseJSON #-}
359−
360−instance (ToJSON a, ToJSON b) => ToJSON (Either a b) where
361− toJSON (Left a) = object [left .= a]
362− toJSON (Right b) = object [right .= b]
363− {-# INLINE toJSON #-}
364−
365−instance (FromJSON a, FromJSON b) => FromJSON (Either a b) where
366− parseJSON (Object (M.toList -> [(key, value)]))
367− | key == left = Left <$> parseJSON value
368− | key == right = Right <$> parseJSON value
369− parseJSON _ = mzero
370− {-# INLINE parseJSON #-}
371−
372−left, right :: Text
373−left = "Left"
374−right = "Right"
375−
376−instance ToJSON Bool where
377− toJSON = Bool
378− {-# INLINE toJSON #-}
379−
380−instance FromJSON Bool where
381− parseJSON (Bool b) = pure b
382− parseJSON v = typeMismatch "Bool" v
383− {-# INLINE parseJSON #-}
384−
385−instance ToJSON () where
386− toJSON _ = emptyArray
387− {-# INLINE toJSON #-}
388−
389−instance FromJSON () where
390− parseJSON (Array v) | V.null v = pure ()
391− parseJSON v = typeMismatch "()" v
392− {-# INLINE parseJSON #-}
393−
394−instance ToJSON [Char] where
395− toJSON = String . T.pack
396− {-# INLINE toJSON #-}
397−
398−instance FromJSON [Char] where
399− parseJSON (String t) = pure (T.unpack t)
400− parseJSON v = typeMismatch "String" v
401− {-# INLINE parseJSON #-}
402−
403−instance ToJSON Char where
404− toJSON = String . T.singleton
405− {-# INLINE toJSON #-}
406−
407−instance FromJSON Char where
408− parseJSON (String t)
409− | T.compareLength t 1 == EQ = pure (T.head t)
410− parseJSON v = typeMismatch "Char" v
411− {-# INLINE parseJSON #-}
412−
413−instance ToJSON Double where
414− toJSON = Number . D
415− {-# INLINE toJSON #-}
416−
417−instance FromJSON Double where
418− parseJSON (Number n) = case n of
419− D d -> pure d
420− I i -> pure (fromIntegral i)
421− parseJSON Null = pure (0/0)
422− parseJSON v = typeMismatch "Double" v
423− {-# INLINE parseJSON #-}
424−
425−instance ToJSON Number where
426− toJSON = Number
427− {-# INLINE toJSON #-}
428−
429−instance FromJSON Number where
430− parseJSON (Number n) = pure n
431− parseJSON Null = pure (D (0/0))
432− parseJSON v = typeMismatch "Number" v
433− {-# INLINE parseJSON #-}
434−
435−instance ToJSON Float where
436− toJSON = Number . realToFrac
437− {-# INLINE toJSON #-}
438−
439−instance FromJSON Float where
440− parseJSON (Number n) = pure $ case n of
441− D d -> realToFrac d
442− I i -> fromIntegral i
443− parseJSON Null = pure (0/0)
444− parseJSON v = typeMismatch "Float" v
445− {-# INLINE parseJSON #-}
446−
447−instance ToJSON (Ratio Integer) where
448− toJSON = Number . fromRational
449− {-# INLINE toJSON #-}
450−
451−instance FromJSON (Ratio Integer) where
452− parseJSON (Number n) = pure $ case n of
453− D d -> toRational d
454− I i -> fromIntegral i
455− parseJSON v = typeMismatch "Ratio Integer" v
456− {-# INLINE parseJSON #-}
457−
458−instance ToJSON Int where
459− toJSON = Number . fromIntegral
460− {-# INLINE toJSON #-}
461−
462−instance FromJSON Int where
463− parseJSON = parseIntegral
464− {-# INLINE parseJSON #-}
465−
466−parseIntegral :: Integral a => Value -> Parser a
467−parseIntegral (Number n) = pure (floor n)
468−parseIntegral v = typeMismatch "Integral" v
469−{-# INLINE parseIntegral #-}
470−
471−instance ToJSON Integer where
472− toJSON = Number . fromIntegral
473− {-# INLINE toJSON #-}
474−
475−instance FromJSON Integer where
476− parseJSON = parseIntegral
477− {-# INLINE parseJSON #-}
478−
479−instance ToJSON Int8 where
480− toJSON = Number . fromIntegral
481− {-# INLINE toJSON #-}
482−
483−instance FromJSON Int8 where
484− parseJSON = parseIntegral
485− {-# INLINE parseJSON #-}
486−
487−instance ToJSON Int16 where
488− toJSON = Number . fromIntegral
489− {-# INLINE toJSON #-}
490−
491−instance FromJSON Int16 where
492− parseJSON = parseIntegral
493− {-# INLINE parseJSON #-}
494−
495−instance ToJSON Int32 where
496− toJSON = Number . fromIntegral
497− {-# INLINE toJSON #-}
498−
499−instance FromJSON Int32 where
500− parseJSON = parseIntegral
501− {-# INLINE parseJSON #-}
502−
503−instance ToJSON Int64 where
504− toJSON = Number . fromIntegral
505− {-# INLINE toJSON #-}
506−
507−instance FromJSON Int64 where
508− parseJSON = parseIntegral
509− {-# INLINE parseJSON #-}
510−
511−instance ToJSON Word where
512− toJSON = Number . fromIntegral
513− {-# INLINE toJSON #-}
514−
515−instance FromJSON Word where
516− parseJSON = parseIntegral
517− {-# INLINE parseJSON #-}
518−
519−instance ToJSON Word8 where
520− toJSON = Number . fromIntegral
521− {-# INLINE toJSON #-}
522−
523−instance FromJSON Word8 where
524− parseJSON = parseIntegral
525− {-# INLINE parseJSON #-}
526−
527−instance ToJSON Word16 where
528− toJSON = Number . fromIntegral
529− {-# INLINE toJSON #-}
530−
531−instance FromJSON Word16 where
532− parseJSON = parseIntegral
533− {-# INLINE parseJSON #-}
534−
535−instance ToJSON Word32 where
536− toJSON = Number . fromIntegral
537− {-# INLINE toJSON #-}
538−
539−instance FromJSON Word32 where
540− parseJSON = parseIntegral
541− {-# INLINE parseJSON #-}
542−
543−instance ToJSON Word64 where
544− toJSON = Number . fromIntegral
545− {-# INLINE toJSON #-}
546−
547−instance FromJSON Word64 where
548− parseJSON = parseIntegral
549− {-# INLINE parseJSON #-}
550−
551−instance ToJSON Text where
552− toJSON = String
553− {-# INLINE toJSON #-}
554−
555−instance FromJSON Text where
556− parseJSON (String t) = pure t
557− parseJSON v = typeMismatch "Text" v
558− {-# INLINE parseJSON #-}
559−
560−instance ToJSON LT.Text where
561− toJSON = String . LT.toStrict
562− {-# INLINE toJSON #-}
563−
564−instance FromJSON LT.Text where
565− parseJSON (String t) = pure (LT.fromStrict t)
566− parseJSON v = typeMismatch "Lazy Text" v
567− {-# INLINE parseJSON #-}
568−
569−instance ToJSON B.ByteString where
570− toJSON = String . decode
571− {-# INLINE toJSON #-}
572−
573−instance FromJSON B.ByteString where
574− parseJSON (String t) = pure . encodeUtf8 $ t
575− parseJSON v = typeMismatch "ByteString" v
576− {-# INLINE parseJSON #-}
577−
578−instance ToJSON LB.ByteString where
579− toJSON = toJSON . strict
580− {-# INLINE toJSON #-}
581−
582−instance FromJSON LB.ByteString where
583− parseJSON (String t) = pure . lazy $ t
584− parseJSON v = typeMismatch "Lazy ByteString" v
585− {-# INLINE parseJSON #-}
586−
587−instance (ToJSON a) => ToJSON [a] where
588− toJSON = Array . V.fromList . map toJSON
589− {-# INLINE toJSON #-}
590−
591−instance (FromJSON a) => FromJSON [a] where
592− parseJSON (Array a) = mapM parseJSON (V.toList a)
593− parseJSON v = typeMismatch "[a]" v
594− {-# INLINE parseJSON #-}
595−
596−instance (ToJSON a) => ToJSON (Vector a) where
597− toJSON = Array . V.map toJSON
598− {-# INLINE toJSON #-}
599−
600−instance (FromJSON a) => FromJSON (Vector a) where
601− parseJSON (Array a) = V.mapM parseJSON a
602− parseJSON v = typeMismatch "Vector a" v
603− {-# INLINE parseJSON #-}
604−
605−vectorToJSON :: (VG.Vector v a, ToJSON a) => v a -> Value
606−vectorToJSON = Array . V.map toJSON . V.convert
607−{-# INLINE vectorToJSON #-}
608−
609−vectorParseJSON :: (FromJSON a, VG.Vector w a) => String -> Value -> Parser (w a)
610−vectorParseJSON _ (Array a) = V.convert <$> V.mapM parseJSON a
611−vectorParseJSON s v = typeMismatch s v
612−{-# INLINE vectorParseJSON #-}
613−
614−instance (Storable a, ToJSON a) => ToJSON (VS.Vector a) where
615− toJSON = vectorToJSON
616−
617−instance (Storable a, FromJSON a) => FromJSON (VS.Vector a) where
618− parseJSON = vectorParseJSON "Data.Vector.Storable.Vector a"
619−
620−instance (VP.Prim a, ToJSON a) => ToJSON (VP.Vector a) where
621− toJSON = vectorToJSON
622−
623−instance (VP.Prim a, FromJSON a) => FromJSON (VP.Vector a) where
624− parseJSON = vectorParseJSON "Data.Vector.Primitive.Vector a"
625−
626−instance (VG.Vector VU.Vector a, ToJSON a) => ToJSON (VU.Vector a) where
627− toJSON = vectorToJSON
628−
629−instance (VG.Vector VU.Vector a, FromJSON a) => FromJSON (VU.Vector a) where
630− parseJSON = vectorParseJSON "Data.Vector.Unboxed.Vector a"
631−
632−instance (ToJSON a) => ToJSON (Set.Set a) where
633− toJSON = toJSON . Set.toList
634− {-# INLINE toJSON #-}
635−
636−instance (Ord a, FromJSON a) => FromJSON (Set.Set a) where
637− parseJSON = fmap Set.fromList . parseJSON
638− {-# INLINE parseJSON #-}
639−
640−instance (ToJSON a) => ToJSON (HashSet.HashSet a) where
641− toJSON = toJSON . HashSet.toList
642− {-# INLINE toJSON #-}
643−
644−instance (Eq a, Hashable a, FromJSON a) => FromJSON (HashSet.HashSet a) where
645− parseJSON = fmap HashSet.fromList . parseJSON
646− {-# INLINE parseJSON #-}
647−
648−instance ToJSON IntSet.IntSet where
649− toJSON = toJSON . IntSet.toList
650− {-# INLINE toJSON #-}
651−
652−instance FromJSON IntSet.IntSet where
653− parseJSON = fmap IntSet.fromList . parseJSON
654− {-# INLINE parseJSON #-}
655−
656−instance (ToJSON v) => ToJSON (M.Map Text v) where
657− toJSON = Object . M.map toJSON
658− {-# INLINE toJSON #-}
659−
660−instance (FromJSON v) => FromJSON (M.Map Text v) where
661− parseJSON (Object o) = M.fromAscList <$> mapM go (M.toAscList o)
662− where go (k,v) = ((,) k) <$> parseJSON v
663− parseJSON v = typeMismatch "Map Text a" v
664−
665−instance (ToJSON v) => ToJSON (M.Map LT.Text v) where
666− toJSON = Object . transformMap LT.toStrict toJSON
667−
668−instance (FromJSON v) => FromJSON (M.Map LT.Text v) where
669− parseJSON = fmap (M.mapKeysMonotonic LT.fromStrict) . parseJSON
670−
671−instance (ToJSON v) => ToJSON (M.Map String v) where
672− toJSON = Object . transformMap pack toJSON
673−
674−instance (FromJSON v) => FromJSON (M.Map String v) where
675− parseJSON = fmap (M.mapKeysMonotonic unpack) . parseJSON
676−
677−instance (ToJSON v) => ToJSON (M.Map B.ByteString v) where
678− toJSON = Object . transformMap decode toJSON
679−
680−instance (FromJSON v) => FromJSON (M.Map B.ByteString v) where
681− parseJSON = fmap (M.mapKeysMonotonic encodeUtf8) . parseJSON
682−
683−instance (ToJSON v) => ToJSON (M.Map LB.ByteString v) where
684− toJSON = Object . transformMap strict toJSON
685−
686−instance (FromJSON v) => FromJSON (M.Map LB.ByteString v) where
687− parseJSON = fmap (M.mapKeysMonotonic lazy) . parseJSON
688−
689−instance (ToJSON v) => ToJSON (H.HashMap Text v) where
690− toJSON = Object . hashMap id toJSON
691− {-# INLINE toJSON #-}
692−
693−instance (FromJSON v) => FromJSON (H.HashMap Text v) where
694− parseJSON (Object o) = H.fromList <$> mapM go (M.toList o)
695− where go (k,v) = ((,) k) <$> parseJSON v
696− parseJSON v = typeMismatch "HashMap Text a" v
697−
698−instance (ToJSON v) => ToJSON (H.HashMap LT.Text v) where
699− toJSON = Object . M.fromList . H.foldrWithKey (\k v -> ((LT.toStrict k,toJSON v) :)) []
700−
701−instance (FromJSON v) => FromJSON (H.HashMap LT.Text v) where
702− parseJSON = fmap (mapHash LT.fromStrict) . parseJSON
703−
704−instance (ToJSON v) => ToJSON (H.HashMap String v) where
705− toJSON = Object . hashMap pack toJSON
706−
707−instance (FromJSON v) => FromJSON (H.HashMap String v) where
708− parseJSON = fmap (mapHash unpack) . parseJSON
709−
710−instance (ToJSON v) => ToJSON (H.HashMap B.ByteString v) where
711− toJSON = Object . hashMap decode toJSON
712−
713−instance (FromJSON v) => FromJSON (H.HashMap B.ByteString v) where
714− parseJSON = fmap (mapHash encodeUtf8) . parseJSON
715−
716−instance (ToJSON v) => ToJSON (H.HashMap LB.ByteString v) where
717− toJSON = Object . hashMap strict toJSON
718−
719−instance (FromJSON v) => FromJSON (H.HashMap LB.ByteString v) where
720− parseJSON = fmap (mapHash lazy) . parseJSON
721−
722−instance ToJSON Value where
723− toJSON a = a
724− {-# INLINE toJSON #-}
725−
726−instance FromJSON Value where
727− parseJSON a = pure a
728− {-# INLINE parseJSON #-}
729−
730−-- | A newtype wrapper for 'UTCTime' that uses the same non-standard
731−-- serialization format as Microsoft .NET, whose @System.DateTime@
732−-- type is by default serialized to JSON as in the following example:
733−--
734−-- > /Date(1302547608878)/
735−--
736−-- The number represents milliseconds since the Unix epoch.
737−newtype DotNetTime = DotNetTime {
738− fromDotNetTime :: UTCTime
739− } deriving (Eq, Ord, Read, Show, Typeable, FormatTime)
740−
741−instance ToJSON DotNetTime where
742− toJSON (DotNetTime t) =
743− String (pack (secs ++ msecs ++ ")/"))
744− where secs = formatTime defaultTimeLocale "/Date(%s" t
745− msecs = take 3 $ formatTime defaultTimeLocale "%q" t
746− {-# INLINE toJSON #-}
747−
748−instance FromJSON DotNetTime where
749− parseJSON (String t) =
750− case parseTime defaultTimeLocale "/Date(%s%Q)/" (unpack t') of
751− Just d -> pure (DotNetTime d)
752− _ -> fail "could not parse .NET time"
753− where (s,m) = T.splitAt (T.length t - 5) t
754− t' = T.concat [s,".",m]
755− parseJSON v = typeMismatch "DotNetTime" v
756− {-# INLINE parseJSON #-}
757−
758−instance ToJSON UTCTime where
759− toJSON t = String (pack (take 23 str ++ "Z"))
760− where str = formatTime defaultTimeLocale "%FT%T%Q" t
761− {-# INLINE toJSON #-}
762−
763−instance FromJSON UTCTime where
764− parseJSON (String t) =
765− case parseTime defaultTimeLocale "%FT%T%QZ" (unpack t) of
766− Just d -> pure d
767− _ -> fail "could not parse ISO-8601 date"
768− parseJSON v = typeMismatch "UTCTime" v
769− {-# INLINE parseJSON #-}
770−
771−instance (ToJSON a, ToJSON b) => ToJSON (a,b) where
772− toJSON (a,b) = toJSON [toJSON a, toJSON b]
773− {-# INLINE toJSON #-}
774−
775−instance (FromJSON a, FromJSON b) => FromJSON (a,b) where
776− parseJSON (Array ab) =
777− case V.toList ab of
778− [a,b] -> (,) <$> parseJSON a <*> parseJSON b
779− _ -> fail $ "cannot unpack array of length " ++
780− show (V.length ab) ++ " into a pair"
781− parseJSON v = typeMismatch "(a,b)" v
782− {-# INLINE parseJSON #-}
783−
784−instance (ToJSON a, ToJSON b, ToJSON c) => ToJSON (a,b,c) where
785− toJSON (a,b,c) = toJSON [toJSON a, toJSON b, toJSON c]
786− {-# INLINE toJSON #-}
787−
788−instance (FromJSON a, FromJSON b, FromJSON c) => FromJSON (a,b,c) where
789− parseJSON (Array abc) =
790− case V.toList abc of
791− [a,b,c] -> (,,) <$> parseJSON a <*> parseJSON b <*> parseJSON c
792− _ -> fail $ "cannot unpack array of length " ++
793− show (V.length abc) ++ " into a 3-tuple"
794− parseJSON v = typeMismatch "(a,b,c)" v
795− {-# INLINE parseJSON #-}
796−
797−instance ToJSON a => ToJSON (Dual a) where
798− toJSON = toJSON . getDual
799− {-# INLINE toJSON #-}
800−
801−instance FromJSON a => FromJSON (Dual a) where
802− parseJSON = fmap Dual . parseJSON
803− {-# INLINE parseJSON #-}
804−
805−instance ToJSON a => ToJSON (First a) where
806− toJSON = toJSON . getFirst
807− {-# INLINE toJSON #-}
808−
809−instance FromJSON a => FromJSON (First a) where
810− parseJSON = fmap First . parseJSON
811− {-# INLINE parseJSON #-}
812−
813−instance ToJSON a => ToJSON (Last a) where
814− toJSON = toJSON . getLast
815− {-# INLINE toJSON #-}
816−
817−instance FromJSON a => FromJSON (Last a) where
818− parseJSON = fmap Last . parseJSON
819− {-# INLINE parseJSON #-}
820−
821−-- | Fail parsing due to a type mismatch, with a descriptive message.
822−typeMismatch :: String -- ^ The name of the type you are trying to parse.
823− -> Value -- ^ The actual value encountered.
824− -> Parser a
825−typeMismatch expected actual =
826− fail $ "when expecting a " ++ expected ++ ", encountered " ++ name ++
827− " instead"
828− where
829− name = case actual of
830− Object _ -> "Object"
831− Array _ -> "Array"
832− String _ -> "String"
833− Number _ -> "Number"
834− Bool _ -> "Boolean"
835− Null -> "Null"
836−
837−
838−--------------------------------------------------------------------------------
839−-- Generic toJSON and fromJSON
840−
841−type T a = a -> Value
842−
843−genericToJSON :: (Data a) => a -> Value
844−genericToJSON = toJSON_generic
845− `ext1Q` list
846− `ext1Q` vector
847− `ext1Q` set
848− `ext2Q'` mapAny
849− `ext2Q'` hashMapAny
850− -- Use the standard encoding for all base types.
851− `extQ` (toJSON :: T Integer)
852− `extQ` (toJSON :: T Int)
853− `extQ` (toJSON :: T Int8)
854− `extQ` (toJSON :: T Int16)
855− `extQ` (toJSON :: T Int32)
856− `extQ` (toJSON :: T Int64)
857− `extQ` (toJSON :: T Word)
858− `extQ` (toJSON :: T Word8)
859− `extQ` (toJSON :: T Word16)
860− `extQ` (toJSON :: T Word32)
861− `extQ` (toJSON :: T Word64)
862− `extQ` (toJSON :: T Double)
863− `extQ` (toJSON :: T Number)
864− `extQ` (toJSON :: T Float)
865− `extQ` (toJSON :: T Rational)
866− `extQ` (toJSON :: T Char)
867− `extQ` (toJSON :: T Text)
868− `extQ` (toJSON :: T LT.Text)
869− `extQ` (toJSON :: T String)
870− `extQ` (toJSON :: T B.ByteString)
871− `extQ` (toJSON :: T LB.ByteString)
872− `extQ` (toJSON :: T Value)
873− `extQ` (toJSON :: T DotNetTime)
874− `extQ` (toJSON :: T UTCTime)
875− `extQ` (toJSON :: T IntSet)
876− `extQ` (toJSON :: T Bool)
877− `extQ` (toJSON :: T ())
878− --`extQ` (T.toJSON :: T Ordering)
879− where
880− list xs = Array . V.fromList . map genericToJSON $ xs
881− vector v = Array . V.map genericToJSON $ v
882− set s = Array . V.fromList . map genericToJSON . Set.toList $ s
883−
884− mapAny m
885− | tyrep == typeOf T.empty = remap id
886− | tyrep == typeOf LT.empty = remap LT.toStrict
887− | tyrep == typeOf string = remap pack
888− | tyrep == typeOf B.empty = remap decode
889− | tyrep == typeOf LB.empty = remap strict
890− | otherwise = modError "genericToJSON" $
891− "cannot convert map keyed by type " ++ show tyrep
892− where tyrep = typeOf . head . M.keys $ m
893− remap f = Object . transformMap (f . fromJust . cast) genericToJSON $ m
894−
895− hashMapAny m
896− | tyrep == typeOf T.empty = remap id
897− | tyrep == typeOf LT.empty = remap LT.toStrict
898− | tyrep == typeOf string = remap pack
899− | tyrep == typeOf B.empty = remap decode
900− | tyrep == typeOf LB.empty = remap strict
901− | otherwise = modError "genericToJSON" $
902− "cannot convert map keyed by type " ++ show tyrep
903− where tyrep = typeOf . head . H.keys $ m
904− remap f = Object . hashMap (f . fromJust . cast) genericToJSON $ m
905−
906−
907−toJSON_generic :: (Data a) => a -> Value
908−toJSON_generic = generic
909− where
910− -- Generic encoding of an algebraic data type.
911− generic a =
912− case dataTypeRep (dataTypeOf a) of
913− -- No constructor, so it must be an error value. Code
914− -- it anyway as Null.
915− AlgRep [] -> Null
916− -- Elide a single constructor and just code the arguments.
917− AlgRep [c] -> encodeArgs c (gmapQ genericToJSON a)
918− -- For multiple constructors, make an object with a
919− -- field name that is the constructor (except lower
920− -- case) and the data is the arguments encoded.
921− AlgRep _ -> encodeConstr (toConstr a) (gmapQ genericToJSON a)
922− rep -> err (dataTypeOf a) rep
923− where
924− err dt r = modError "genericToJSON" $ "not AlgRep " ++
925− show r ++ "(" ++ show dt ++ ")"
926− -- Encode nullary constructor as a string.
927− -- Encode non-nullary constructors as an object with the constructor
928− -- name as the single field and the arguments as the value.
929− -- Use an array if the are no field names, but elide singleton arrays,
930− -- and use an object if there are field names.
931− encodeConstr c [] = String . constrString $ c
932− encodeConstr c as = object [(constrString c, encodeArgs c as)]
933−
934− constrString = pack . showConstr
935−
936− encodeArgs c = encodeArgs' (constrFields c)
937− encodeArgs' [] [j] = j
938− encodeArgs' [] js = Array . V.fromList $ js
939− encodeArgs' ns js = object $ zip (map mungeField ns) js
940−
941− -- Skip leading '_' in field name so we can use keywords
942− -- etc. as field names.
943− mungeField ('_':cs) = pack cs
944− mungeField cs = pack cs
945−
946−genericFromJSON :: (Data a) => Value -> Result a
947−genericFromJSON = parse genericParseJSON
948−
949−type F a = Parser a
950−
951−genericParseJSON :: (Data a) => Value -> Parser a
952−genericParseJSON j = parseJSON_generic j
953− `ext1R` list
954− `ext1R` vector
955− `ext2R'` mapAny
956− `ext2R'` hashMapAny
957− -- Use the standard encoding for all base types.
958− `extR` (value :: F Integer)
959− `extR` (value :: F Int)
960− `extR` (value :: F Int8)
961− `extR` (value :: F Int16)
962− `extR` (value :: F Int32)
963− `extR` (value :: F Int64)
964− `extR` (value :: F Word)
965− `extR` (value :: F Word8)
966− `extR` (value :: F Word16)
967− `extR` (value :: F Word32)
968− `extR` (value :: F Word64)
969− `extR` (value :: F Double)
970− `extR` (value :: F Number)
971− `extR` (value :: F Float)
972− `extR` (value :: F Rational)
973− `extR` (value :: F Char)
974− `extR` (value :: F Text)
975− `extR` (value :: F LT.Text)
976− `extR` (value :: F String)
977− `extR` (value :: F B.ByteString)
978− `extR` (value :: F LB.ByteString)
979− `extR` (value :: F Value)
980− `extR` (value :: F DotNetTime)
981− `extR` (value :: F UTCTime)
982− `extR` (value :: F IntSet)
983− `extR` (value :: F Bool)
984− `extR` (value :: F ())
985− where
986− value :: (FromJSON a) => Parser a
987− value = parseJSON j
988− list :: (Data a) => Parser [a]
989− list = V.toList <$> genericParseJSON j
990− vector :: (Data a) => Parser (V.Vector a)
991− vector = case j of
992− Array js -> V.mapM genericParseJSON js
993− _ -> myFail
994− mapAny :: forall e f. (Data e, Data f) => Parser (Map f e)
995− mapAny
996− | tyrep `elem` stringyTypes = res
997− | otherwise = myFail
998− where res = case j of
999− Object js -> M.mapKeysMonotonic trans <$> T.mapM genericParseJSON js
1000− _ -> myFail
1001− trans
1002− | tyrep == typeOf T.empty = remap id
1003− | tyrep == typeOf LT.empty = remap LT.fromStrict
1004− | tyrep == typeOf string = remap T.unpack
1005− | tyrep == typeOf B.empty = remap encodeUtf8
1006− | tyrep == typeOf LB.empty = remap lazy
1007− | otherwise = modError "genericParseJSON"
1008− "mapAny -- should never happen"
1009− tyrep = typeOf (undefined :: f)
1010− remap f = fromJust . cast . f
1011− hashMapAny :: forall e f. (Data e, Data f) => Parser (H.HashMap f e)
1012− hashMapAny
1013− | tyrep == typeOf string = process T.unpack
1014− | tyrep == typeOf LT.empty = process LT.fromStrict
1015− | tyrep == typeOf T.empty = process id
1016− | otherwise = myFail
1017− where
1018− process f = maybe myFail return . cast =<< parseWith f
1019− parseWith :: (Eq c, Hashable c) => (Text -> c) -> Parser (H.HashMap c e)
1020− parseWith f = case j of
1021− Object js -> H.fromList . map (first f) . M.toList <$>
1022− T.mapM genericParseJSON js
1023− _ -> myFail
1024− tyrep = typeOf (undefined :: f)
1025− myFail = modFail "genericParseJSON" $ "bad data: " ++ show j
1026− stringyTypes = [typeOf LT.empty, typeOf T.empty, typeOf B.empty,
1027− typeOf LB.empty, typeOf string]
1028−
1029−parseJSON_generic :: (Data a) => Value -> Parser a
1030−parseJSON_generic j = generic
1031− where
1032− typ = dataTypeOf $ resType generic
1033− generic = case dataTypeRep typ of
1034− AlgRep [] -> case j of
1035− Null -> return (modError "genericParseJSON" "empty type")
1036− _ -> modFail "genericParseJSON" "no-constr bad data"
1037− AlgRep [_] -> decodeArgs (indexConstr typ 1) j
1038− AlgRep _ -> do (c, j') <- getConstr typ j; decodeArgs c j'
1039− rep -> modFail "genericParseJSON" $
1040− show rep ++ "(" ++ show typ ++ ")"
1041− getConstr t (Object o) | [(s, j')] <- fromJSObject o = do
1042− c <- readConstr' t s
1043− return (c, j')
1044− getConstr t (String js) = do c <- readConstr' t (unpack js)
1045− return (c, Null) -- handle nullary ctor
1046− getConstr _ _ = modFail "genericParseJSON" "bad constructor encoding"
1047− readConstr' t s =
1048− maybe (modFail "genericParseJSON" $ "unknown constructor: " ++ s ++ " " ++
1049− show t)
1050− return $ readConstr t s
1051−
1052− decodeArgs c0 = go (numConstrArgs (resType generic) c0) c0
1053− (constrFields c0)
1054− where
1055− go 0 c _ Null = construct c [] -- nullary constructor
1056− go 1 c [] jd = construct c [jd] -- unary constructor
1057− go n c [] (Array js)
1058− | n > 1 = construct c (V.toList js) -- no field names
1059− -- FIXME? We could allow reading an array into a constructor
1060− -- with field names.
1061− go _ c fs@(_:_) (Object o) = selectFields o fs >>=
1062− construct c -- field names
1063− go _ c _ jd = modFail "genericParseJSON" $
1064− "bad decodeArgs data " ++ show (c, jd)
1065−
1066− fromJSObject = map (first unpack) . M.toList
1067−
1068− -- Build the value by stepping through the list of subparts.
1069− construct c = evalStateT $ fromConstrM f c
1070− where f :: (Data a) => StateT [Value] Parser a
1071− f = do js <- get
1072− case js of
1073− [] -> lift $ modFail "construct" "empty list"
1074− (j':js') -> do put js'; lift $ genericParseJSON j'
1075−
1076− -- Select the named fields from a JSON object.
1077− selectFields fjs = mapM sel
1078− where sel f = maybe (modFail "genericParseJSON" $ "field does not exist " ++
1079− f) return $ M.lookup (pack f) fjs
1080−
1081− -- Count how many arguments a constructor has. The value x is
1082− -- used to determine what type the constructor returns.
1083− numConstrArgs :: (Data a) => a -> Constr -> Int
1084− numConstrArgs x c = execState (fromConstrM f c `asTypeOf` return x) 0
1085− where f = do modify (+1); return undefined
1086−
1087− resType :: MonadPlus m => m a -> a
1088− resType _ = modError "genericParseJSON" "resType"
1089−
1090−modFail :: (Monad m) => String -> String -> m a
1091−modFail func err = fail $ "Data.Aeson.Types." ++ func ++ ": " ++ err
1092−
1093−modError :: String -> String -> a
1094−modError func err = error $ "Data.Aeson.Types." ++ func ++ ": " ++ err
1095−
1096−string :: String
1097−string = ""
1098−
1099−-- Type extension for binary type constructors.
1100−
1101−-- | Flexible type extension
1102−ext2' :: (Data a, Typeable2 t)
1103− => c a
1104− -> (forall d1 d2. (Data d1, Data d2) => c (t d1 d2))
1105− -> c a
1106−ext2' def ext = maybe def id (dataCast2 ext)
1107−
1108−-- | Type extension of queries for type constructors
1109−ext2Q' :: (Data d, Typeable2 t)
1110− => (d -> q)
1111− -> (forall d1 d2. (Data d1, Data d2) => t d1 d2 -> q)
1112− -> d -> q
1113−ext2Q' def ext = unQ ((Q def) `ext2'` (Q ext))
1114−
1115−-- | Type extension of readers for type constructors
1116−ext2R' :: (Monad m, Data d, Typeable2 t)
1117− => m d
1118− -> (forall d1 d2. (Data d1, Data d2) => m (t d1 d2))
1119− -> m d
1120−ext2R' def ext = unR ((R def) `ext2'` (R ext))
1121−
1122−-- | The type constructor for queries
1123−newtype Q q x = Q { unQ :: x -> q }
1124−
1125−-- | The type constructor for readers
1126−newtype R m x = R { unR :: m x }
Data/Aeson/Types/Class.hsnew file+776
@@ -0,0 +1,776 @@
1+{-# LANGUAGE CPP, DeriveDataTypeable, FlexibleContexts, FlexibleInstances,
2+ GeneralizedNewtypeDeriving, IncoherentInstances, OverlappingInstances,
3+ OverloadedStrings, UndecidableInstances, ViewPatterns #-}
4+
5+#ifdef GENERICS
6+{-# LANGUAGE DefaultSignatures #-}
7+#endif
8+
9+-- |
10+-- Module: Data.Aeson.Types.Class
11+-- Copyright: (c) 2011 MailRank, Inc.
12+-- License: Apache
13+-- Maintainer: Bryan O'Sullivan <bos@serpentine.com>
14+-- Stability: experimental
15+-- Portability: portable
16+--
17+-- Types for working with JSON data.
18+
19+module Data.Aeson.Types.Class
20+ (
21+ -- * Type classes
22+ -- ** Core JSON classes
23+ FromJSON(..)
24+ , ToJSON(..)
25+#ifdef GENERICS
26+ -- ** Generic JSON classes
27+ , GFromJSON(..)
28+ , GToJSON(..)
29+#endif
30+ -- * Types
31+ , DotNetTime(..)
32+ -- * Functions
33+ , fromJSON
34+ , (.:)
35+ , (.:?)
36+ , (.:/)
37+ , (.=)
38+ , typeMismatch
39+ ) where
40+
41+import Control.Applicative ((<$>), (<*>), pure)
42+import Data.Aeson.Functions
43+import Data.Aeson.Types.Internal
44+import Data.Attoparsec.Char8 (Number(..))
45+import Data.Hashable (Hashable(..))
46+import Data.Int (Int8, Int16, Int32, Int64)
47+import Data.Monoid (Dual(..), First(..), Last(..))
48+import Data.Ratio (Ratio)
49+import Data.Text (Text, pack, unpack)
50+import Data.Text.Encoding (encodeUtf8)
51+import Data.Time.Clock (UTCTime)
52+import Data.Time.Format (FormatTime, formatTime, parseTime)
53+import Data.Traversable (traverse)
54+import Data.Typeable (Typeable)
55+import Data.Vector (Vector)
56+import Data.Word (Word, Word8, Word16, Word32, Word64)
57+import Foreign.Storable (Storable)
58+import System.Locale (defaultTimeLocale)
59+import qualified Data.ByteString as B
60+import qualified Data.ByteString.Lazy as LB
61+import qualified Data.HashMap.Strict as H
62+import qualified Data.HashSet as HashSet
63+import qualified Data.IntMap as IntMap
64+import qualified Data.IntSet as IntSet
65+import qualified Data.Map as M
66+import qualified Data.Set as Set
67+import qualified Data.Text as T
68+import qualified Data.Text.Lazy as LT
69+import qualified Data.Vector as V
70+import qualified Data.Vector.Generic as VG
71+import qualified Data.Vector.Primitive as VP
72+import qualified Data.Vector.Storable as VS
73+import qualified Data.Vector.Unboxed as VU
74+import qualified Data.Vector.Mutable as VM ( unsafeNew, unsafeWrite )
75+
76+#ifdef GENERICS
77+import GHC.Generics
78+
79+class GToJSON f where
80+ gToJSON :: f a -> Value
81+
82+class GFromJSON f where
83+ gParseJSON :: Value -> Parser (f a)
84+#endif
85+
86+-- | A type that can be converted to JSON.
87+--
88+-- An example type and instance:
89+--
90+-- @{-\# LANGUAGE OverloadedStrings #-}
91+--
92+-- data Coord { x :: Double, y :: Double }
93+--
94+-- instance ToJSON Coord where
95+-- toJSON (Coord x y) = 'object' [\"x\" '.=' x, \"y\" '.=' y]
96+-- @
97+--
98+-- Note the use of the @OverloadedStrings@ language extension which enables
99+-- 'Text' values to be written as string literals.
100+--
101+-- Instead of manually writing your 'ToJSON' instance, there are three options
102+-- to do it automatically:
103+--
104+-- * 'Data.Aeson.TH' provides template-haskell functions which will derive an
105+-- instance at compile-time. The generated instance is optimized for your type
106+-- so will probably be more efficient than the following two options:
107+--
108+-- * 'Data.Aeson.Generic' provides a generic @toJSON@ function that accepts any
109+-- type which is an instance of 'Data'.
110+--
111+-- * If your compiler has support for the @DeriveGeneric@ and
112+-- @DefaultSignatures@ language extensions, @toJSON@ will have a default generic
113+-- implementation.
114+--
115+-- To use the latter option, simply add a @deriving 'Generic'@ clause to your
116+-- datatype and declare a @ToJSON@ instance for your datatype without giving a
117+-- definition for @toJSON@.
118+--
119+-- For example the previous example can be simplified to just:
120+--
121+-- @{-\# LANGUAGE DeriveGeneric \#-}
122+--
123+-- import GHC.Generics
124+--
125+-- data Coord { x :: Double, y :: Double } deriving Generic
126+--
127+-- instance ToJSON Coord
128+-- @
129+class ToJSON a where
130+ toJSON :: a -> Value
131+
132+#ifdef GENERICS
133+ default toJSON :: (Generic a, GToJSON (Rep a)) => a -> Value
134+ toJSON = gToJSON . from
135+#endif
136+
137+-- | A type that can be converted from JSON, with the possibility of
138+-- failure.
139+--
140+-- When writing an instance, use 'empty', 'mzero', or 'fail' to make a
141+-- conversion fail, e.g. if an 'Object' is missing a required key, or
142+-- the value is of the wrong type.
143+--
144+-- An example type and instance:
145+--
146+-- @{-\# LANGUAGE OverloadedStrings #-}
147+--
148+-- data Coord { x :: Double, y :: Double }
149+--
150+-- instance FromJSON Coord where
151+-- parseJSON ('Object' v) = Coord '<$>'
152+-- v '.:' \"x\" '<*>'
153+-- v '.:' \"y\"
154+--
155+-- \-- A non-'Object' value is of the wrong type, so use 'mzero' to fail.
156+-- parseJSON _ = 'mzero'
157+-- @
158+--
159+-- Note the use of the @OverloadedStrings@ language extension which enables
160+-- 'Text' values to be written as string literals.
161+--
162+-- Instead of manually writing your 'FromJSON' instance, there are three options
163+-- to do it automatically:
164+--
165+-- * 'Data.Aeson.TH' provides template-haskell functions which will derive an
166+-- instance at compile-time. The generated instance is optimized for your type
167+-- so will probably be more efficient than the following two options:
168+--
169+-- * 'Data.Aeson.Generic' provides a generic @fromJSON@ function that parses to
170+-- any type which is an instance of 'Data'.
171+--
172+-- * If your compiler has support for the @DeriveGeneric@ and
173+-- @DefaultSignatures@ language extensions, @parseJSON@ will have a default
174+-- generic implementation.
175+--
176+-- To use this, simply add a @deriving 'Generic'@ clause to your datatype and
177+-- declare a @FromJSON@ instance for your datatype without giving a definition
178+-- for @parseJSON@.
179+--
180+-- For example the previous example can be simplified to just:
181+--
182+-- @{-\# LANGUAGE DeriveGeneric \#-}
183+--
184+-- import GHC.Generics
185+--
186+-- data Coord { x :: Double, y :: Double } deriving Generic
187+--
188+-- instance FromJSON Coord
189+-- @
190+class FromJSON a where
191+ parseJSON :: Value -> Parser a
192+
193+#ifdef GENERICS
194+ default parseJSON :: (Generic a, GFromJSON (Rep a)) => Value -> Parser a
195+ parseJSON = fmap to . gParseJSON
196+#endif
197+
198+instance (ToJSON a) => ToJSON (Maybe a) where
199+ toJSON (Just a) = toJSON a
200+ toJSON Nothing = Null
201+ {-# INLINE toJSON #-}
202+
203+instance (FromJSON a) => FromJSON (Maybe a) where
204+ parseJSON Null = pure Nothing
205+ parseJSON a = Just <$> parseJSON a
206+ {-# INLINE parseJSON #-}
207+
208+instance (ToJSON a, ToJSON b) => ToJSON (Either a b) where
209+ toJSON (Left a) = object [left .= a]
210+ toJSON (Right b) = object [right .= b]
211+ {-# INLINE toJSON #-}
212+
213+instance (FromJSON a, FromJSON b) => FromJSON (Either a b) where
214+ parseJSON (Object (H.toList -> [(key, value)]))
215+ | key == left = Left <$> parseJSON value
216+ | key == right = Right <$> parseJSON value
217+ parseJSON _ = fail ""
218+ {-# INLINE parseJSON #-}
219+
220+left, right :: Text
221+left = "Left"
222+right = "Right"
223+
224+instance ToJSON Bool where
225+ toJSON = Bool
226+ {-# INLINE toJSON #-}
227+
228+instance FromJSON Bool where
229+ parseJSON (Bool b) = pure b
230+ parseJSON v = typeMismatch "Bool" v
231+ {-# INLINE parseJSON #-}
232+
233+instance ToJSON () where
234+ toJSON _ = emptyArray
235+ {-# INLINE toJSON #-}
236+
237+instance FromJSON () where
238+ parseJSON (Array v) | V.null v = pure ()
239+ parseJSON v = typeMismatch "()" v
240+ {-# INLINE parseJSON #-}
241+
242+instance ToJSON [Char] where
243+ toJSON = String . T.pack
244+ {-# INLINE toJSON #-}
245+
246+instance FromJSON [Char] where
247+ parseJSON (String t) = pure (T.unpack t)
248+ parseJSON v = typeMismatch "String" v
249+ {-# INLINE parseJSON #-}
250+
251+instance ToJSON Char where
252+ toJSON = String . T.singleton
253+ {-# INLINE toJSON #-}
254+
255+instance FromJSON Char where
256+ parseJSON (String t)
257+ | T.compareLength t 1 == EQ = pure (T.head t)
258+ parseJSON v = typeMismatch "Char" v
259+ {-# INLINE parseJSON #-}
260+
261+instance ToJSON Double where
262+ toJSON = Number . D
263+ {-# INLINE toJSON #-}
264+
265+instance FromJSON Double where
266+ parseJSON (Number n) = case n of
267+ D d -> pure d
268+ I i -> pure (fromIntegral i)
269+ parseJSON Null = pure (0/0)
270+ parseJSON v = typeMismatch "Double" v
271+ {-# INLINE parseJSON #-}
272+
273+instance ToJSON Number where
274+ toJSON = Number
275+ {-# INLINE toJSON #-}
276+
277+instance FromJSON Number where
278+ parseJSON (Number n) = pure n
279+ parseJSON Null = pure (D (0/0))
280+ parseJSON v = typeMismatch "Number" v
281+ {-# INLINE parseJSON #-}
282+
283+instance ToJSON Float where
284+ toJSON = Number . realToFrac
285+ {-# INLINE toJSON #-}
286+
287+instance FromJSON Float where
288+ parseJSON (Number n) = pure $ case n of
289+ D d -> realToFrac d
290+ I i -> fromIntegral i
291+ parseJSON Null = pure (0/0)
292+ parseJSON v = typeMismatch "Float" v
293+ {-# INLINE parseJSON #-}
294+
295+instance ToJSON (Ratio Integer) where
296+ toJSON = Number . fromRational
297+ {-# INLINE toJSON #-}
298+
299+instance FromJSON (Ratio Integer) where
300+ parseJSON (Number n) = pure $ case n of
301+ D d -> toRational d
302+ I i -> fromIntegral i
303+ parseJSON v = typeMismatch "Ratio Integer" v
304+ {-# INLINE parseJSON #-}
305+
306+instance ToJSON Int where
307+ toJSON = Number . fromIntegral
308+ {-# INLINE toJSON #-}
309+
310+instance FromJSON Int where
311+ parseJSON = parseIntegral
312+ {-# INLINE parseJSON #-}
313+
314+parseIntegral :: Integral a => Value -> Parser a
315+parseIntegral (Number n) = pure (floor n)
316+parseIntegral v = typeMismatch "Integral" v
317+{-# INLINE parseIntegral #-}
318+
319+instance ToJSON Integer where
320+ toJSON = Number . fromIntegral
321+ {-# INLINE toJSON #-}
322+
323+instance FromJSON Integer where
324+ parseJSON = parseIntegral
325+ {-# INLINE parseJSON #-}
326+
327+instance ToJSON Int8 where
328+ toJSON = Number . fromIntegral
329+ {-# INLINE toJSON #-}
330+
331+instance FromJSON Int8 where
332+ parseJSON = parseIntegral
333+ {-# INLINE parseJSON #-}
334+
335+instance ToJSON Int16 where
336+ toJSON = Number . fromIntegral
337+ {-# INLINE toJSON #-}
338+
339+instance FromJSON Int16 where
340+ parseJSON = parseIntegral
341+ {-# INLINE parseJSON #-}
342+
343+instance ToJSON Int32 where
344+ toJSON = Number . fromIntegral
345+ {-# INLINE toJSON #-}
346+
347+instance FromJSON Int32 where
348+ parseJSON = parseIntegral
349+ {-# INLINE parseJSON #-}
350+
351+instance ToJSON Int64 where
352+ toJSON = Number . fromIntegral
353+ {-# INLINE toJSON #-}
354+
355+instance FromJSON Int64 where
356+ parseJSON = parseIntegral
357+ {-# INLINE parseJSON #-}
358+
359+instance ToJSON Word where
360+ toJSON = Number . fromIntegral
361+ {-# INLINE toJSON #-}
362+
363+instance FromJSON Word where
364+ parseJSON = parseIntegral
365+ {-# INLINE parseJSON #-}
366+
367+instance ToJSON Word8 where
368+ toJSON = Number . fromIntegral
369+ {-# INLINE toJSON #-}
370+
371+instance FromJSON Word8 where
372+ parseJSON = parseIntegral
373+ {-# INLINE parseJSON #-}
374+
375+instance ToJSON Word16 where
376+ toJSON = Number . fromIntegral
377+ {-# INLINE toJSON #-}
378+
379+instance FromJSON Word16 where
380+ parseJSON = parseIntegral
381+ {-# INLINE parseJSON #-}
382+
383+instance ToJSON Word32 where
384+ toJSON = Number . fromIntegral
385+ {-# INLINE toJSON #-}
386+
387+instance FromJSON Word32 where
388+ parseJSON = parseIntegral
389+ {-# INLINE parseJSON #-}
390+
391+instance ToJSON Word64 where
392+ toJSON = Number . fromIntegral
393+ {-# INLINE toJSON #-}
394+
395+instance FromJSON Word64 where
396+ parseJSON = parseIntegral
397+ {-# INLINE parseJSON #-}
398+
399+instance ToJSON Text where
400+ toJSON = String
401+ {-# INLINE toJSON #-}
402+
403+instance FromJSON Text where
404+ parseJSON (String t) = pure t
405+ parseJSON v = typeMismatch "Text" v
406+ {-# INLINE parseJSON #-}
407+
408+instance ToJSON LT.Text where
409+ toJSON = String . LT.toStrict
410+ {-# INLINE toJSON #-}
411+
412+instance FromJSON LT.Text where
413+ parseJSON (String t) = pure (LT.fromStrict t)
414+ parseJSON v = typeMismatch "Lazy Text" v
415+ {-# INLINE parseJSON #-}
416+
417+instance ToJSON B.ByteString where
418+ toJSON = String . decode
419+ {-# INLINE toJSON #-}
420+
421+instance FromJSON B.ByteString where
422+ parseJSON (String t) = pure . encodeUtf8 $ t
423+ parseJSON v = typeMismatch "ByteString" v
424+ {-# INLINE parseJSON #-}
425+
426+instance ToJSON LB.ByteString where
427+ toJSON = toJSON . strict
428+ {-# INLINE toJSON #-}
429+
430+instance FromJSON LB.ByteString where
431+ parseJSON (String t) = pure . lazy $ t
432+ parseJSON v = typeMismatch "Lazy ByteString" v
433+ {-# INLINE parseJSON #-}
434+
435+instance (ToJSON a) => ToJSON [a] where
436+ toJSON = Array . V.fromList . map toJSON
437+ {-# INLINE toJSON #-}
438+
439+instance (FromJSON a) => FromJSON [a] where
440+ parseJSON (Array a) = mapM parseJSON (V.toList a)
441+ parseJSON v = typeMismatch "[a]" v
442+ {-# INLINE parseJSON #-}
443+
444+instance (ToJSON a) => ToJSON (Vector a) where
445+ toJSON = Array . V.map toJSON
446+ {-# INLINE toJSON #-}
447+
448+instance (FromJSON a) => FromJSON (Vector a) where
449+ parseJSON (Array a) = V.mapM parseJSON a
450+ parseJSON v = typeMismatch "Vector a" v
451+ {-# INLINE parseJSON #-}
452+
453+vectorToJSON :: (VG.Vector v a, ToJSON a) => v a -> Value
454+vectorToJSON = Array . V.map toJSON . V.convert
455+{-# INLINE vectorToJSON #-}
456+
457+vectorParseJSON :: (FromJSON a, VG.Vector w a) => String -> Value -> Parser (w a)
458+vectorParseJSON _ (Array a) = V.convert <$> V.mapM parseJSON a
459+vectorParseJSON s v = typeMismatch s v
460+{-# INLINE vectorParseJSON #-}
461+
462+instance (Storable a, ToJSON a) => ToJSON (VS.Vector a) where
463+ toJSON = vectorToJSON
464+
465+instance (Storable a, FromJSON a) => FromJSON (VS.Vector a) where
466+ parseJSON = vectorParseJSON "Data.Vector.Storable.Vector a"
467+
468+instance (VP.Prim a, ToJSON a) => ToJSON (VP.Vector a) where
469+ toJSON = vectorToJSON
470+
471+instance (VP.Prim a, FromJSON a) => FromJSON (VP.Vector a) where
472+ parseJSON = vectorParseJSON "Data.Vector.Primitive.Vector a"
473+
474+instance (VG.Vector VU.Vector a, ToJSON a) => ToJSON (VU.Vector a) where
475+ toJSON = vectorToJSON
476+
477+instance (VG.Vector VU.Vector a, FromJSON a) => FromJSON (VU.Vector a) where
478+ parseJSON = vectorParseJSON "Data.Vector.Unboxed.Vector a"
479+
480+instance (ToJSON a) => ToJSON (Set.Set a) where
481+ toJSON = toJSON . Set.toList
482+ {-# INLINE toJSON #-}
483+
484+instance (Ord a, FromJSON a) => FromJSON (Set.Set a) where
485+ parseJSON = fmap Set.fromList . parseJSON
486+ {-# INLINE parseJSON #-}
487+
488+instance (ToJSON a) => ToJSON (HashSet.HashSet a) where
489+ toJSON = toJSON . HashSet.toList
490+ {-# INLINE toJSON #-}
491+
492+instance (Eq a, Hashable a, FromJSON a) => FromJSON (HashSet.HashSet a) where
493+ parseJSON = fmap HashSet.fromList . parseJSON
494+ {-# INLINE parseJSON #-}
495+
496+instance ToJSON IntSet.IntSet where
497+ toJSON = toJSON . IntSet.toList
498+ {-# INLINE toJSON #-}
499+
500+instance FromJSON IntSet.IntSet where
501+ parseJSON = fmap IntSet.fromList . parseJSON
502+ {-# INLINE parseJSON #-}
503+
504+instance ToJSON a => ToJSON (IntMap.IntMap a) where
505+ toJSON = toJSON . IntMap.toList
506+ {-# INLINE toJSON #-}
507+
508+instance FromJSON a => FromJSON (IntMap.IntMap a) where
509+ parseJSON = fmap IntMap.fromList . parseJSON
510+ {-# INLINE parseJSON #-}
511+
512+instance (ToJSON v) => ToJSON (M.Map Text v) where
513+ toJSON = Object . M.foldrWithKey (\k -> H.insert k . toJSON) H.empty
514+ {-# INLINE toJSON #-}
515+
516+instance (FromJSON v) => FromJSON (M.Map Text v) where
517+ parseJSON (Object o) = H.foldrWithKey M.insert M.empty <$> traverse parseJSON o
518+ parseJSON v = typeMismatch "Map Text a" v
519+
520+instance (ToJSON v) => ToJSON (M.Map LT.Text v) where
521+ toJSON = Object . mapHashKeyVal LT.toStrict toJSON
522+
523+instance (FromJSON v) => FromJSON (M.Map LT.Text v) where
524+ parseJSON = fmap (hashMapKey LT.fromStrict) . parseJSON
525+
526+instance (ToJSON v) => ToJSON (M.Map String v) where
527+ toJSON = Object . mapHashKeyVal pack toJSON
528+
529+instance (FromJSON v) => FromJSON (M.Map String v) where
530+ parseJSON = fmap (hashMapKey unpack) . parseJSON
531+
532+instance (ToJSON v) => ToJSON (M.Map B.ByteString v) where
533+ toJSON = Object . mapHashKeyVal decode toJSON
534+
535+instance (FromJSON v) => FromJSON (M.Map B.ByteString v) where
536+ parseJSON = fmap (hashMapKey encodeUtf8) . parseJSON
537+
538+instance (ToJSON v) => ToJSON (M.Map LB.ByteString v) where
539+ toJSON = Object . mapHashKeyVal strict toJSON
540+
541+instance (FromJSON v) => FromJSON (M.Map LB.ByteString v) where
542+ parseJSON = fmap (hashMapKey lazy) . parseJSON
543+
544+instance (ToJSON v) => ToJSON (H.HashMap Text v) where
545+ toJSON = Object . H.map toJSON
546+ {-# INLINE toJSON #-}
547+
548+instance (FromJSON v) => FromJSON (H.HashMap Text v) where
549+ parseJSON (Object o) = traverse parseJSON o
550+ parseJSON v = typeMismatch "HashMap Text a" v
551+
552+instance (ToJSON v) => ToJSON (H.HashMap LT.Text v) where
553+ toJSON = Object . mapKeyVal LT.toStrict toJSON
554+
555+instance (FromJSON v) => FromJSON (H.HashMap LT.Text v) where
556+ parseJSON = fmap (mapKey LT.fromStrict) . parseJSON
557+
558+instance (ToJSON v) => ToJSON (H.HashMap String v) where
559+ toJSON = Object . mapKeyVal pack toJSON
560+
561+instance (FromJSON v) => FromJSON (H.HashMap String v) where
562+ parseJSON = fmap (mapKey unpack) . parseJSON
563+
564+instance (ToJSON v) => ToJSON (H.HashMap B.ByteString v) where
565+ toJSON = Object . mapKeyVal decode toJSON
566+
567+instance (FromJSON v) => FromJSON (H.HashMap B.ByteString v) where
568+ parseJSON = fmap (mapKey encodeUtf8) . parseJSON
569+
570+instance (ToJSON v) => ToJSON (H.HashMap LB.ByteString v) where
571+ toJSON = Object . mapKeyVal strict toJSON
572+
573+instance (FromJSON v) => FromJSON (H.HashMap LB.ByteString v) where
574+ parseJSON = fmap (mapKey lazy) . parseJSON
575+
576+instance ToJSON Value where
577+ toJSON a = a
578+ {-# INLINE toJSON #-}
579+
580+instance FromJSON Value where
581+ parseJSON a = pure a
582+ {-# INLINE parseJSON #-}
583+
584+-- | A newtype wrapper for 'UTCTime' that uses the same non-standard
585+-- serialization format as Microsoft .NET, whose @System.DateTime@
586+-- type is by default serialized to JSON as in the following example:
587+--
588+-- > /Date(1302547608878)/
589+--
590+-- The number represents milliseconds since the Unix epoch.
591+newtype DotNetTime = DotNetTime {
592+ fromDotNetTime :: UTCTime
593+ } deriving (Eq, Ord, Read, Show, Typeable, FormatTime)
594+
595+instance ToJSON DotNetTime where
596+ toJSON (DotNetTime t) =
597+ String (pack (secs ++ msecs ++ ")/"))
598+ where secs = formatTime defaultTimeLocale "/Date(%s" t
599+ msecs = take 3 $ formatTime defaultTimeLocale "%q" t
600+ {-# INLINE toJSON #-}
601+
602+instance FromJSON DotNetTime where
603+ parseJSON (String t) =
604+ case parseTime defaultTimeLocale "/Date(%s%Q)/" (unpack t') of
605+ Just d -> pure (DotNetTime d)
606+ _ -> fail "could not parse .NET time"
607+ where (s,m) = T.splitAt (T.length t - 5) t
608+ t' = T.concat [s,".",m]
609+ parseJSON v = typeMismatch "DotNetTime" v
610+ {-# INLINE parseJSON #-}
611+
612+instance ToJSON UTCTime where
613+ toJSON t = String (pack (take 23 str ++ "Z"))
614+ where str = formatTime defaultTimeLocale "%FT%T%Q" t
615+ {-# INLINE toJSON #-}
616+
617+instance FromJSON UTCTime where
618+ parseJSON (String t) =
619+ case parseTime defaultTimeLocale "%FT%T%QZ" (unpack t) of
620+ Just d -> pure d
621+ _ -> fail "could not parse ISO-8601 date"
622+ parseJSON v = typeMismatch "UTCTime" v
623+ {-# INLINE parseJSON #-}
624+
625+instance (ToJSON a, ToJSON b) => ToJSON (a,b) where
626+ toJSON (a,b) = Array $ V.create $ do
627+ mv <- VM.unsafeNew 2
628+ VM.unsafeWrite mv 0 (toJSON a)
629+ VM.unsafeWrite mv 1 (toJSON b)
630+ return mv
631+ {-# INLINE toJSON #-}
632+
633+instance (FromJSON a, FromJSON b) => FromJSON (a,b) where
634+ parseJSON (Array ab)
635+ | n == 2 = (,) <$> parseJSON (V.unsafeIndex ab 0)
636+ <*> parseJSON (V.unsafeIndex ab 1)
637+ | otherwise = fail $ "cannot unpack array of length " ++
638+ show n ++ " into a pair"
639+ where
640+ n = V.length ab
641+ parseJSON v = typeMismatch "(a,b)" v
642+ {-# INLINE parseJSON #-}
643+
644+instance (ToJSON a, ToJSON b, ToJSON c) => ToJSON (a,b,c) where
645+ toJSON (a,b,c) = Array $ V.create $ do
646+ mv <- VM.unsafeNew 3
647+ VM.unsafeWrite mv 0 (toJSON a)
648+ VM.unsafeWrite mv 1 (toJSON b)
649+ VM.unsafeWrite mv 2 (toJSON c)
650+ return mv
651+ {-# INLINE toJSON #-}
652+
653+instance (FromJSON a, FromJSON b, FromJSON c) => FromJSON (a,b,c) where
654+ parseJSON (Array abc)
655+ | n == 3 = (,,) <$> parseJSON (V.unsafeIndex abc 0)
656+ <*> parseJSON (V.unsafeIndex abc 1)
657+ <*> parseJSON (V.unsafeIndex abc 2)
658+ | otherwise = fail $ "cannot unpack array of length " ++
659+ show n ++ " into a 3-tuple"
660+ where
661+ n = V.length abc
662+ parseJSON v = typeMismatch "(a,b,c)" v
663+ {-# INLINE parseJSON #-}
664+
665+instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d) => ToJSON (a,b,c,d) where
666+ toJSON (a,b,c,d) = Array $ V.create $ do
667+ mv <- VM.unsafeNew 4
668+ VM.unsafeWrite mv 0 (toJSON a)
669+ VM.unsafeWrite mv 1 (toJSON b)
670+ VM.unsafeWrite mv 2 (toJSON c)
671+ VM.unsafeWrite mv 3 (toJSON d)
672+ return mv
673+ {-# INLINE toJSON #-}
674+
675+instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d) => FromJSON (a,b,c,d) where
676+ parseJSON (Array abcd)
677+ | n == 4 = (,,,) <$> parseJSON (V.unsafeIndex abcd 0)
678+ <*> parseJSON (V.unsafeIndex abcd 1)
679+ <*> parseJSON (V.unsafeIndex abcd 2)
680+ <*> parseJSON (V.unsafeIndex abcd 3)
681+ | otherwise = fail $ "cannot unpack array of length " ++
682+ show n ++ " into a 4-tuple"
683+ where
684+ n = V.length abcd
685+ parseJSON v = typeMismatch "(a,b,c,d)" v
686+ {-# INLINE parseJSON #-}
687+
688+instance ToJSON a => ToJSON (Dual a) where
689+ toJSON = toJSON . getDual
690+ {-# INLINE toJSON #-}
691+
692+instance FromJSON a => FromJSON (Dual a) where
693+ parseJSON = fmap Dual . parseJSON
694+ {-# INLINE parseJSON #-}
695+
696+instance ToJSON a => ToJSON (First a) where
697+ toJSON = toJSON . getFirst
698+ {-# INLINE toJSON #-}
699+
700+instance FromJSON a => FromJSON (First a) where
701+ parseJSON = fmap First . parseJSON
702+ {-# INLINE parseJSON #-}
703+
704+instance ToJSON a => ToJSON (Last a) where
705+ toJSON = toJSON . getLast
706+ {-# INLINE toJSON #-}
707+
708+instance FromJSON a => FromJSON (Last a) where
709+ parseJSON = fmap Last . parseJSON
710+ {-# INLINE parseJSON #-}
711+
712+-- | Construct a 'Pair' from a key and a value.
713+(.=) :: ToJSON a => Text -> a -> Pair
714+name .= value = (name, toJSON value)
715+{-# INLINE (.=) #-}
716+
717+-- | Convert a value from JSON, failing if the types do not match.
718+fromJSON :: (FromJSON a) => Value -> Result a
719+fromJSON = parse parseJSON
720+{-# INLINE fromJSON #-}
721+
722+-- | Retrieve the value associated with the given key of an 'Object'.
723+-- The result is 'empty' if the key is not present or the value cannot
724+-- be converted to the desired type.
725+--
726+-- This accessor is appropriate if the key and value /must/ be present
727+-- in an object for it to be valid. If the key and value are
728+-- optional, use '(.:?)' instead.
729+(.:) :: (FromJSON a) => Object -> Text -> Parser a
730+obj .: key = case H.lookup key obj of
731+ Nothing -> fail $ "key " ++ show key ++ " not present"
732+ Just v -> parseJSON v
733+{-# INLINE (.:) #-}
734+
735+-- | Retrieve the value associated with the given key of an 'Object'.
736+-- The result is 'Nothing' if the key is not present, or 'empty' if
737+-- the value cannot be converted to the desired type.
738+--
739+-- This accessor is most useful if the key and value can be absent
740+-- from an object without affecting its validity. If the key and
741+-- value are mandatory, use '(.:)' instead.
742+(.:?) :: (FromJSON a) => Object -> Text -> Parser (Maybe a)
743+obj .:? key = case H.lookup key obj of
744+ Nothing -> pure Nothing
745+ Just v -> parseJSON v
746+{-# INLINE (.:?) #-}
747+
748+-- | Retrieve the value associated with the given key of an 'Object'.
749+-- The result is a default value if the key is not present, or 'empty'
750+-- if the value cannot be converted to the desired type.
751+--
752+-- This accessor is most useful if the key and value can be absent
753+-- from an object without affecting its validity and we know a
754+-- default value to assign in that case. If the key and value
755+-- are mandatory, use '(.:)' instead.
756+(.:/) :: (FromJSON a) => Object -> (Text, a) -> Parser a
757+obj .:/ (key, val) = case H.lookup key obj of
758+ Nothing -> pure val
759+ Just v -> parseJSON v
760+{-# INLINE (.:/) #-}
761+
762+-- | Fail parsing due to a type mismatch, with a descriptive message.
763+typeMismatch :: String -- ^ The name of the type you are trying to parse.
764+ -> Value -- ^ The actual value encountered.
765+ -> Parser a
766+typeMismatch expected actual =
767+ fail $ "when expecting a " ++ expected ++ ", encountered " ++ name ++
768+ " instead"
769+ where
770+ name = case actual of
771+ Object _ -> "Object"
772+ Array _ -> "Array"
773+ String _ -> "String"
774+ Number _ -> "Number"
775+ Bool _ -> "Boolean"
776+ Null -> "Null"
Data/Aeson/Types/Generic.hsnew file+284
@@ -0,0 +1,284 @@
1+{-# LANGUAGE DefaultSignatures
2+ , EmptyDataDecls
3+ , FlexibleInstances
4+ , FunctionalDependencies
5+ , KindSignatures
6+ , OverlappingInstances
7+ , ScopedTypeVariables
8+ , TypeOperators
9+ , UndecidableInstances
10+ , ViewPatterns
11+ #-}
12+
13+{-# OPTIONS_GHC -fno-warn-orphans #-}
14+
15+-- |
16+-- Module: Data.Aeson.Types.Generic
17+-- Copyright: (c) 2011 MailRank, Inc.
18+-- License: Apache
19+-- Maintainer: Bryan O'Sullivan <bos@serpentine.com>
20+-- Stability: experimental
21+-- Portability: portable
22+--
23+-- Types for working with JSON data.
24+
25+module Data.Aeson.Types.Generic ( ) where
26+
27+import Control.Applicative
28+import Control.Monad.State.Strict
29+import Data.Bits (shiftR)
30+import Data.Aeson.Types.Class
31+import Data.Aeson.Types.Internal
32+import Data.Text (pack, unpack)
33+import Data.DList (DList, toList)
34+import Data.Monoid (mappend)
35+import GHC.Generics
36+import Control.Monad.ST (ST)
37+import qualified Data.HashMap.Strict as H
38+import qualified Data.Text as T
39+import qualified Data.Vector as V
40+import qualified Data.Vector.Mutable as VM
41+
42+--------------------------------------------------------------------------------
43+-- Generic toJSON
44+
45+instance (GToJSON a) => GToJSON (M1 i c a) where
46+ gToJSON = gToJSON . unM1
47+ {-# INLINE gToJSON #-}
48+
49+instance (ToJSON a) => GToJSON (K1 i a) where
50+ gToJSON = toJSON . unK1
51+ {-# INLINE gToJSON #-}
52+
53+instance GToJSON U1 where
54+ gToJSON _ = emptyArray
55+ {-# INLINE gToJSON #-}
56+
57+instance (ConsToJSON a) => GToJSON (C1 c a) where
58+ gToJSON = consToJSON . unM1
59+ {-# INLINE gToJSON #-}
60+
61+instance ( GProductToValues a, GProductToValues b
62+ , ProductSize a, ProductSize b) => GToJSON (a :*: b) where
63+ gToJSON p = Array $ V.create $ do
64+ mv <- VM.unsafeNew lenProduct
65+ gProductToValues mv 0 lenProduct p
66+ return mv
67+ where
68+ lenProduct = unTagged2 (productSize :: Tagged2 (a :*: b) Int)
69+ {-# INLINE gToJSON #-}
70+
71+instance (GObject a, GObject b) => GToJSON (a :+: b) where
72+ gToJSON (L1 x) = Object $ gObject x
73+ gToJSON (R1 x) = Object $ gObject x
74+ {-# INLINE gToJSON #-}
75+
76+--------------------------------------------------------------------------------
77+
78+class ConsToJSON f where consToJSON :: f a -> Value
79+class ConsToJSON' b f where consToJSON' :: Tagged b (f a -> Value)
80+
81+newtype Tagged s b = Tagged {unTagged :: b}
82+
83+instance (IsRecord f b, ConsToJSON' b f) => ConsToJSON f where
84+ consToJSON = unTagged (consToJSON' :: Tagged b (f a -> Value))
85+ {-# INLINE consToJSON #-}
86+
87+instance (GRecordToPairs f) => ConsToJSON' True f where
88+ consToJSON' = Tagged (object . toList . gRecordToPairs)
89+ {-# INLINE consToJSON' #-}
90+
91+instance GToJSON f => ConsToJSON' False f where
92+ consToJSON' = Tagged gToJSON
93+ {-# INLINE consToJSON' #-}
94+
95+--------------------------------------------------------------------------------
96+
97+class GRecordToPairs f where
98+ gRecordToPairs :: f a -> DList Pair
99+
100+instance (GRecordToPairs a, GRecordToPairs b) => GRecordToPairs (a :*: b) where
101+ gRecordToPairs (a :*: b) = gRecordToPairs a `mappend` gRecordToPairs b
102+ {-# INLINE gRecordToPairs #-}
103+
104+instance (Selector s, GToJSON a) => GRecordToPairs (S1 s a) where
105+ gRecordToPairs m1 = pure (pack (selName m1), gToJSON (unM1 m1))
106+ {-# INLINE gRecordToPairs #-}
107+
108+--------------------------------------------------------------------------------
109+
110+class GProductToValues f where
111+ gProductToValues :: VM.MVector s Value -> Int -> Int -> f a -> ST s ()
112+
113+instance (GProductToValues a, GProductToValues b) => GProductToValues (a :*: b) where
114+ gProductToValues mv ix len (a :*: b) = do gProductToValues mv ix lenL a
115+ gProductToValues mv ixR lenR b
116+ where
117+ lenL = len `shiftR` 1
118+ ixR = ix + lenL
119+ lenR = len - lenL
120+ {-# INLINE gProductToValues #-}
121+
122+instance (GToJSON a) => GProductToValues a where
123+ gProductToValues mv ix _ = VM.unsafeWrite mv ix . gToJSON
124+ {-# INLINE gProductToValues #-}
125+
126+--------------------------------------------------------------------------------
127+
128+class GObject f where
129+ gObject :: f a -> Object
130+
131+instance (GObject a, GObject b) => GObject (a :+: b) where
132+ gObject (L1 x) = gObject x
133+ gObject (R1 x) = gObject x
134+ {-# INLINE gObject #-}
135+
136+instance (Constructor c, GToJSON a, ConsToJSON a) => GObject (C1 c a) where
137+ gObject = H.singleton (pack $ conName (undefined :: t c a p)) . gToJSON
138+ {-# INLINE gObject #-}
139+
140+--------------------------------------------------------------------------------
141+-- Generic parseJSON
142+
143+instance (GFromJSON a) => GFromJSON (M1 i c a) where
144+ gParseJSON = fmap M1 . gParseJSON
145+ {-# INLINE gParseJSON #-}
146+
147+instance (FromJSON a) => GFromJSON (K1 i a) where
148+ gParseJSON = fmap K1 . parseJSON
149+ {-# INLINE gParseJSON #-}
150+
151+instance GFromJSON U1 where
152+ gParseJSON v
153+ | isEmptyArray v = pure U1
154+ | otherwise = typeMismatch "unit constructor (U1)" v
155+ {-# INLINE gParseJSON #-}
156+
157+instance (ConsFromJSON a) => GFromJSON (C1 c a) where
158+ gParseJSON = fmap M1 . consParseJSON
159+ {-# INLINE gParseJSON #-}
160+
161+instance ( GFromProduct a, GFromProduct b
162+ , ProductSize a, ProductSize b) => GFromJSON (a :*: b) where
163+ gParseJSON (Array arr)
164+ | lenArray == lenProduct = gParseProduct arr 0 lenProduct
165+ | otherwise =
166+ fail $ "When expecting a product of " ++ show lenProduct ++
167+ " values, encountered an Array of " ++ show lenArray ++
168+ " elements instead"
169+ where
170+ lenArray = V.length arr
171+ lenProduct = unTagged2 (productSize :: Tagged2 (a :*: b) Int)
172+
173+ gParseJSON v = typeMismatch "product (:*:)" v
174+ {-# INLINE gParseJSON #-}
175+
176+instance (GFromSum a, GFromSum b) => GFromJSON (a :+: b) where
177+ gParseJSON (Object (H.toList -> [keyVal@(key, _)])) =
178+ case gParseSum keyVal of
179+ Nothing -> notFound $ unpack key
180+ Just p -> p
181+ gParseJSON v = typeMismatch "sum (:+:)" v
182+ {-# INLINE gParseJSON #-}
183+
184+notFound :: String -> Parser a
185+notFound key = fail $ "The key \"" ++ key ++ "\" was not found"
186+{-# INLINE notFound #-}
187+
188+--------------------------------------------------------------------------------
189+
190+class ConsFromJSON f where consParseJSON :: Value -> Parser (f a)
191+class ConsFromJSON' b f where consParseJSON' :: Tagged b (Value -> Parser (f a))
192+
193+instance (IsRecord f b, ConsFromJSON' b f) => ConsFromJSON f where
194+ consParseJSON = unTagged (consParseJSON' :: Tagged b (Value -> Parser (f a)))
195+ {-# INLINE consParseJSON #-}
196+
197+instance (GFromRecord f) => ConsFromJSON' True f where
198+ consParseJSON' = Tagged parseRecord
199+ where
200+ parseRecord (Object obj) = gParseRecord obj
201+ parseRecord v = typeMismatch "record (:*:)" v
202+ {-# INLINE consParseJSON' #-}
203+
204+instance (GFromJSON f) => ConsFromJSON' False f where
205+ consParseJSON' = Tagged gParseJSON
206+ {-# INLINE consParseJSON' #-}
207+
208+--------------------------------------------------------------------------------
209+
210+class GFromRecord f where
211+ gParseRecord :: Object -> Parser (f a)
212+
213+instance (GFromRecord a, GFromRecord b) => GFromRecord (a :*: b) where
214+ gParseRecord obj = (:*:) <$> gParseRecord obj <*> gParseRecord obj
215+ {-# INLINE gParseRecord #-}
216+
217+instance (Selector s, GFromJSON a) => GFromRecord (S1 s a) where
218+ gParseRecord = maybe (notFound key) gParseJSON . H.lookup (T.pack key)
219+ where
220+ key = selName (undefined :: t s a p)
221+ {-# INLINE gParseRecord #-}
222+
223+--------------------------------------------------------------------------------
224+
225+class ProductSize f where
226+ productSize :: Tagged2 f Int
227+
228+newtype Tagged2 (s :: * -> *) b = Tagged2 {unTagged2 :: b}
229+
230+instance (ProductSize a, ProductSize b) => ProductSize (a :*: b) where
231+ productSize = Tagged2 $ unTagged2 (productSize :: Tagged2 a Int) +
232+ unTagged2 (productSize :: Tagged2 b Int)
233+
234+instance ProductSize (S1 s a) where
235+ productSize = Tagged2 1
236+
237+--------------------------------------------------------------------------------
238+
239+class GFromProduct f where
240+ gParseProduct :: Array -> Int -> Int -> Parser (f a)
241+
242+instance (GFromProduct a, GFromProduct b) => GFromProduct (a :*: b) where
243+ gParseProduct arr ix len = (:*:) <$> gParseProduct arr ix lenL
244+ <*> gParseProduct arr ixR lenR
245+ where
246+ lenL = len `shiftR` 1
247+ ixR = ix + lenL
248+ lenR = len - lenL
249+ {-# INLINE gParseProduct #-}
250+
251+instance (GFromJSON a) => GFromProduct (S1 s a) where
252+ gParseProduct arr ix _ = gParseJSON $ V.unsafeIndex arr ix
253+ {-# INLINE gParseProduct #-}
254+
255+--------------------------------------------------------------------------------
256+
257+class GFromSum f where
258+ gParseSum :: Pair -> Maybe (Parser (f a))
259+
260+instance (GFromSum a, GFromSum b) => GFromSum (a :+: b) where
261+ gParseSum keyVal = (fmap L1 <$> gParseSum keyVal) <|>
262+ (fmap R1 <$> gParseSum keyVal)
263+ {-# INLINE gParseSum #-}
264+
265+instance (Constructor c, GFromJSON a, ConsFromJSON a) => GFromSum (C1 c a) where
266+ gParseSum (key, value)
267+ | key == pack (conName (undefined :: t c a p)) = Just $ gParseJSON value
268+ | otherwise = Nothing
269+ {-# INLINE gParseSum #-}
270+
271+--------------------------------------------------------------------------------
272+
273+class IsRecord (f :: * -> *) b | f -> b
274+
275+data True
276+data False
277+
278+instance (IsRecord f b) => IsRecord (f :*: g) b
279+instance IsRecord (M1 S NoSelector f) False
280+instance (IsRecord f b) => IsRecord (M1 S c f) b
281+instance IsRecord (K1 i c) True
282+instance IsRecord U1 False
283+
284+--------------------------------------------------------------------------------
Data/Aeson/Types/Internal.hsnew file+222
@@ -0,0 +1,222 @@
1+{-# LANGUAGE CPP, DeriveDataTypeable, Rank2Types #-}
2+
3+-- |
4+-- Module: Data.Aeson.Types.Internal
5+-- Copyright: (c) 2011 MailRank, Inc.
6+-- License: Apache
7+-- Maintainer: Bryan O'Sullivan <bos@serpentine.com>
8+-- Stability: experimental
9+-- Portability: portable
10+--
11+-- Types for working with JSON data.
12+
13+module Data.Aeson.Types.Internal
14+ (
15+ -- * Core JSON types
16+ Value(..)
17+ , Array
18+ , emptyArray, isEmptyArray
19+ , Pair
20+ , Object
21+ , emptyObject
22+ -- * Type conversion
23+ , Parser
24+ , Result(..)
25+ , parse
26+ , parseEither
27+ , parseMaybe
28+ -- * Constructors and accessors
29+ , object
30+ ) where
31+
32+import Control.Applicative
33+import Control.DeepSeq (NFData(..))
34+import Control.Monad.State.Strict
35+import Data.Attoparsec.Char8 (Number(..))
36+import Data.Hashable (Hashable(..))
37+import Data.HashMap.Strict (HashMap)
38+import Data.Monoid (Monoid(..))
39+import Data.String (IsString(..))
40+import Data.Text (Text, pack)
41+import Data.Typeable (Typeable)
42+import Data.Vector (Vector)
43+import qualified Data.HashMap.Strict as H
44+import qualified Data.Vector as V
45+
46+-- | The result of running a 'Parser'.
47+data Result a = Error String
48+ | Success a
49+ deriving (Eq, Show, Typeable)
50+
51+instance (NFData a) => NFData (Result a) where
52+ rnf (Success a) = rnf a
53+ rnf (Error err) = rnf err
54+
55+instance Functor Result where
56+ fmap f (Success a) = Success (f a)
57+ fmap _ (Error err) = Error err
58+ {-# INLINE fmap #-}
59+
60+instance Monad Result where
61+ return = Success
62+ {-# INLINE return #-}
63+ Success a >>= k = k a
64+ Error err >>= _ = Error err
65+ {-# INLINE (>>=) #-}
66+
67+instance Applicative Result where
68+ pure = return
69+ {-# INLINE pure #-}
70+ (<*>) = ap
71+ {-# INLINE (<*>) #-}
72+
73+instance MonadPlus Result where
74+ mzero = fail "mzero"
75+ {-# INLINE mzero #-}
76+ mplus a@(Success _) _ = a
77+ mplus _ b = b
78+ {-# INLINE mplus #-}
79+
80+instance Alternative Result where
81+ empty = mzero
82+ {-# INLINE empty #-}
83+ (<|>) = mplus
84+ {-# INLINE (<|>) #-}
85+
86+instance Monoid (Result a) where
87+ mempty = fail "mempty"
88+ {-# INLINE mempty #-}
89+ mappend = mplus
90+ {-# INLINE mappend #-}
91+
92+-- | Failure continuation.
93+type Failure f r = String -> f r
94+-- | Success continuation.
95+type Success a f r = a -> f r
96+
97+-- | A continuation-based parser type.
98+newtype Parser a = Parser {
99+ runParser :: forall f r.
100+ Failure f r
101+ -> Success a f r
102+ -> f r
103+ }
104+
105+instance Monad Parser where
106+ m >>= g = Parser $ \kf ks -> let ks' a = runParser (g a) kf ks
107+ in runParser m kf ks'
108+ {-# INLINE (>>=) #-}
109+ return a = Parser $ \_kf ks -> ks a
110+ {-# INLINE return #-}
111+ fail msg = Parser $ \kf _ks -> kf msg
112+ {-# INLINE fail #-}
113+
114+instance Functor Parser where
115+ fmap f m = Parser $ \kf ks -> let ks' a = ks (f a)
116+ in runParser m kf ks'
117+ {-# INLINE fmap #-}
118+
119+instance Applicative Parser where
120+ pure = return
121+ {-# INLINE pure #-}
122+ (<*>) = apP
123+ {-# INLINE (<*>) #-}
124+
125+instance Alternative Parser where
126+ empty = fail "empty"
127+ {-# INLINE empty #-}
128+ (<|>) = mplus
129+ {-# INLINE (<|>) #-}
130+
131+instance MonadPlus Parser where
132+ mzero = fail "mzero"
133+ {-# INLINE mzero #-}
134+ mplus a b = Parser $ \kf ks -> let kf' _ = runParser b kf ks
135+ in runParser a kf' ks
136+ {-# INLINE mplus #-}
137+
138+instance Monoid (Parser a) where
139+ mempty = fail "mempty"
140+ {-# INLINE mempty #-}
141+ mappend = mplus
142+ {-# INLINE mappend #-}
143+
144+apP :: Parser (a -> b) -> Parser a -> Parser b
145+apP d e = do
146+ b <- d
147+ a <- e
148+ return (b a)
149+{-# INLINE apP #-}
150+
151+-- | A JSON \"object\" (key\/value map).
152+type Object = HashMap Text Value
153+
154+-- | A JSON \"array\" (sequence).
155+type Array = Vector Value
156+
157+-- | A JSON value represented as a Haskell value.
158+data Value = Object !Object
159+ | Array !Array
160+ | String !Text
161+ | Number !Number
162+ | Bool !Bool
163+ | Null
164+ deriving (Eq, Show, Typeable)
165+
166+instance NFData Value where
167+ rnf (Object o) = rnf o
168+ rnf (Array a) = V.foldl' (\x y -> rnf y `seq` x) () a
169+ rnf (String s) = rnf s
170+ rnf (Number n) = case n of I i -> rnf i; D d -> rnf d
171+ rnf (Bool b) = rnf b
172+ rnf Null = ()
173+
174+instance IsString Value where
175+ fromString = String . pack
176+ {-# INLINE fromString #-}
177+
178+instance Hashable Value where
179+ hash (Object o) = H.foldl' hashWithSalt 0 o
180+ hash (Array a) = V.foldl' hashWithSalt 1 a
181+ hash (String s) = 2 `hashWithSalt` s
182+ hash (Number n) = 3 `hashWithSalt` case n of I i -> hash i; D d -> hash d
183+ hash (Bool b) = 4 `hashWithSalt` b
184+ hash Null = 5
185+
186+-- | The empty array.
187+emptyArray :: Value
188+emptyArray = Array V.empty
189+
190+-- | Determines if the 'Value' is an empty 'Array'.
191+-- Note that: @isEmptyArray 'emptyArray'@.
192+isEmptyArray :: Value -> Bool
193+isEmptyArray (Array arr) = V.null arr
194+isEmptyArray _ = False
195+
196+-- | The empty object.
197+emptyObject :: Value
198+emptyObject = Object H.empty
199+
200+-- | Run a 'Parser'.
201+parse :: (a -> Parser b) -> a -> Result b
202+parse m v = runParser (m v) Error Success
203+{-# INLINE parse #-}
204+
205+-- | Run a 'Parser' with a 'Maybe' result type.
206+parseMaybe :: (a -> Parser b) -> a -> Maybe b
207+parseMaybe m v = runParser (m v) (const Nothing) Just
208+{-# INLINE parseMaybe #-}
209+
210+-- | Run a 'Parser' with an 'Either' result type.
211+parseEither :: (a -> Parser b) -> a -> Either String b
212+parseEither m v = runParser (m v) Left Right
213+
214+-- | A key\/value pair for an 'Object'.
215+type Pair = (Text, Value)
216+
217+{-# INLINE parseEither #-}
218+-- | Create a 'Value' from a list of name\/value 'Pair's. If duplicate
219+-- keys arise, earlier keys and their associated values win.
220+object :: [Pair] -> Value
221+object = Object . H.fromList
222+{-# INLINE object #-}
README.markdown+5−5
@@ -11,7 +11,7 @@ in your work.
1111
1212For complete details, including a workaround, see the writeup in [the
1313blaze-textual
14−README](https://github.com/mailrank/blaze-textual#readme).
14+README](https://github.com/bos/blaze-textual#readme).
1515
1616# Join in!
1717
@@ -19,11 +19,11 @@ We are happy to receive bug reports, fixes, documentation enhancements,
1919and other improvements.
2020
2121Please report bugs via the
22−[github issue tracker](http://github.com/mailrank/aeson/issues).
22+[github issue tracker](http://github.com/bos/aeson/issues).
2323
24−Master [git repository](http://github.com/mailrank/aeson):
24+Master [git repository](http://github.com/bos/aeson):
2525
26−* `git clone git://github.com/mailrank/aeson.git`
26+* `git clone git://github.com/bos/aeson.git`
2727
2828There's also a [Mercurial mirror](http://bitbucket.org/bos/aeson):
2929
@@ -35,4 +35,4 @@ Authors
3535-------
3636
3737This library is written and maintained by Bryan O'Sullivan,
38−<bos@mailrank.com>.
38+<bos@serpentine.com>.
aeson.cabal+41−14
@@ -1,26 +1,29 @@
11name: aeson
2−version: 0.3.2.11
2+version: 0.4.0.0
33license: BSD3
44license-file: LICENSE
55category: Text, Web, JSON
66copyright: Copyright 2011 MailRank, Inc.
7−author: Bryan O'Sullivan <bos@mailrank.com>
8−maintainer: Bryan O'Sullivan <bos@mailrank.com>
7+author: Bryan O'Sullivan <bos@serpentine.com>
8+maintainer: Bryan O'Sullivan <bos@serpentine.com>
99stability: experimental
1010tested-with: GHC == 6.12.3
1111synopsis: Fast JSON parsing and encoding
1212cabal-version: >= 1.8
13−homepage: http://github.com/mailrank/aeson
14−bug-reports: http://github.com/mailrank/aeson/issues
13+homepage: http://github.com/bos/aeson
14+bug-reports: http://github.com/bos/aeson/issues
1515build-type: Simple
1616description:
1717 A JSON parsing and encoding library optimized for ease of use
1818 and high performance.
1919 .
20+ To get started, see the documentation for the @Data.Aeson@ module
21+ below.
22+ .
2023 /Note/: if you use GHCi or Template Haskell, please see the
2124 @README@ file for important details about building this package,
2225 and other packages that depend on it:
23− <https://github.com/mailrank/aeson#readme>
26+ <https://github.com/bos/aeson#readme>
2427 .
2528 Parsing performance on a late 2010 MacBook Pro (2.66GHz Core i7),
2629 for mostly-English tweets from Twitter's JSON search API:
@@ -94,7 +97,6 @@ extra-source-files:
9497 benchmarks/json-data/twitter20.json
9598 benchmarks/json-data/twitter50.json
9699 benchmarks/json-data/twitter100.json
97− tests/Makefile
98100 tests/Properties.hs
99101 examples/Demo.hs
100102
@@ -113,6 +115,14 @@ library
113115
114116 other-modules:
115117 Data.Aeson.Functions
118+ Data.Aeson.Types.Class
119+ Data.Aeson.Types.Internal
120+
121+ if impl(ghc >= 7.2.1)
122+ cpp-options: -DGENERICS
123+ build-depends: ghc-prim >= 0.2, dlist >= 0.2
124+ other-modules:
125+ Data.Aeson.Types.Generic
116126
117127 build-depends:
118128 attoparsec >= 0.8.6.1,
@@ -121,29 +131,46 @@ library
121131 blaze-textual >= 0.2.0.2,
122132 bytestring,
123133 containers,
124− deepseq < 1.2,
134+ deepseq,
125135 hashable >= 1.1.2.0,
126136 mtl,
127137 old-locale,
128138 syb,
139+ template-haskell >= 2.4,
129140 text >= 0.11.0.2,
130− template-haskell >= 2.5,
131141 time,
132142 unordered-containers >= 0.1.3.0,
133− vector >= 0.7
143+ vector >= 0.7.1
134144
135145 if flag(developer)
136146 ghc-options: -Werror
137147 ghc-prof-options: -auto-all
138148
139− if(impl(ghc >= 7.2.1))
140− cpp-options: -DDEFAULT_SIGNATURES
141−
142149 ghc-options: -Wall
143150
151+test-suite tests
152+ type: exitcode-stdio-1.0
153+ hs-source-dirs: tests
154+ main-is: Properties.hs
155+
156+ ghc-options:
157+ -Wall -threaded -rtsopts
158+
159+ build-depends:
160+ QuickCheck,
161+ aeson,
162+ attoparsec,
163+ base,
164+ containers,
165+ bytestring,
166+ template-haskell,
167+ test-framework,
168+ test-framework-quickcheck2,
169+ text
170+
144171source-repository head
145172 type: git
146− location: http://github.com/mailrank/aeson
173+ location: http://github.com/bos/aeson
147174
148175source-repository head
149176 type: mercurial
benchmarks/AesonCompareAutoInstances.hsnew file+198
@@ -0,0 +1,198 @@
1+{-# LANGUAGE DeriveDataTypeable, DeriveGeneric, TemplateHaskell #-}
2+
3+module Main where
4+
5+--------------------------------------------------------------------------------
6+
7+import Criterion.Main
8+
9+import Control.DeepSeq (NFData, rnf, deepseq)
10+
11+import Data.Typeable (Typeable)
12+import Data.Data (Data)
13+import GHC.Generics (Generic)
14+
15+import Data.Aeson.Types
16+import Data.Aeson.TH (mkToJSON, mkParseJSON)
17+import qualified Data.Aeson.Generic as G (fromJSON, toJSON)
18+
19+--------------------------------------------------------------------------------
20+
21+-- Taken from the documentation of Data.Aeson.TH:
22+data D a = Nullary
23+ | Unary Int
24+ | Product String Char a
25+ | Record { testOne :: Double
26+ , testTwo :: Bool
27+ , testThree :: D a
28+ } deriving (Show, Eq, Generic, Data, Typeable)
29+
30+instance NFData a => NFData (D a) where
31+ rnf Nullary = ()
32+ rnf (Unary n) = rnf n
33+ rnf (Product s c x) = s `deepseq` c `deepseq` rnf x
34+ rnf (Record d b y) = d `deepseq` b `deepseq` rnf y
35+
36+type T = D (D (D ()))
37+
38+d :: T
39+d = Record
40+ { testOne = 1234.56789
41+ , testTwo = True
42+ , testThree = Product "Hello World!" 'a' $
43+ Record
44+ { testOne = 9876.54321
45+ , testTwo = False
46+ , testThree = Product "Yeehaa!!!" '\n' Nullary
47+ }
48+ }
49+
50+instance ToJSON a => ToJSON (D a)
51+instance FromJSON a => FromJSON (D a)
52+
53+thDToJSON :: ToJSON a => D a -> Value
54+thDToJSON = $(mkToJSON id ''D)
55+
56+thDParseJSON :: FromJSON a => Value -> Parser (D a)
57+thDParseJSON = $(mkParseJSON id ''D)
58+
59+thDFromJSON :: FromJSON a => Value -> Result (D a)
60+thDFromJSON = parse thDParseJSON
61+
62+--------------------------------------------------------------------------------
63+
64+data BigRecord = BigRecord
65+ { field01 :: !Int, field02 :: !Int, field03 :: !Int, field04 :: !Int, field05 :: !Int
66+ , field06 :: !Int, field07 :: !Int, field08 :: !Int, field09 :: !Int, field10 :: !Int
67+ , field11 :: !Int, field12 :: !Int, field13 :: !Int, field14 :: !Int, field15 :: !Int
68+ , field16 :: !Int, field17 :: !Int, field18 :: !Int, field19 :: !Int, field20 :: !Int
69+ , field21 :: !Int, field22 :: !Int, field23 :: !Int, field24 :: !Int, field25 :: !Int
70+ } deriving (Show, Eq, Generic, Data, Typeable)
71+
72+instance NFData BigRecord
73+
74+bigRecord = BigRecord 1 2 3 4 5
75+ 6 7 8 9 10
76+ 11 12 13 14 15
77+ 16 17 18 19 20
78+ 21 22 23 24 25
79+
80+instance ToJSON BigRecord
81+instance FromJSON BigRecord
82+
83+thBigRecordToJSON :: BigRecord -> Value
84+thBigRecordToJSON = $(mkToJSON id ''BigRecord)
85+
86+thBigRecordParseJSON :: Value -> Parser BigRecord
87+thBigRecordParseJSON = $(mkParseJSON id ''BigRecord)
88+
89+thBigRecordFromJSON :: Value -> Result BigRecord
90+thBigRecordFromJSON = parse thBigRecordParseJSON
91+
92+--------------------------------------------------------------------------------
93+
94+data BigProduct = BigProduct
95+ !Int !Int !Int !Int !Int
96+ !Int !Int !Int !Int !Int
97+ !Int !Int !Int !Int !Int
98+ !Int !Int !Int !Int !Int
99+ !Int !Int !Int !Int !Int
100+ deriving (Show, Eq, Generic, Data, Typeable)
101+
102+instance NFData BigProduct
103+
104+bigProduct = BigProduct 1 2 3 4 5
105+ 6 7 8 9 10
106+ 11 12 13 14 15
107+ 16 17 18 19 20
108+ 21 22 23 24 25
109+
110+instance ToJSON BigProduct
111+instance FromJSON BigProduct
112+
113+thBigProductToJSON :: BigProduct -> Value
114+thBigProductToJSON = $(mkToJSON id ''BigProduct)
115+
116+thBigProductParseJSON :: Value -> Parser BigProduct
117+thBigProductParseJSON = $(mkParseJSON id ''BigProduct)
118+
119+thBigProductFromJSON :: Value -> Result BigProduct
120+thBigProductFromJSON = parse thBigProductParseJSON
121+
122+--------------------------------------------------------------------------------
123+
124+data BigSum = F01 | F02 | F03 | F04 | F05
125+ | F06 | F07 | F08 | F09 | F10
126+ | F11 | F12 | F13 | F14 | F15
127+ | F16 | F17 | F18 | F19 | F20
128+ | F21 | F22 | F23 | F24 | F25
129+ deriving (Show, Eq, Generic, Data, Typeable)
130+
131+instance NFData BigSum
132+
133+bigSum = F25
134+
135+instance ToJSON BigSum
136+instance FromJSON BigSum
137+
138+thBigSumToJSON :: BigSum -> Value
139+thBigSumToJSON = $(mkToJSON id ''BigSum)
140+
141+thBigSumParseJSON :: Value -> Parser BigSum
142+thBigSumParseJSON = $(mkParseJSON id ''BigSum)
143+
144+thBigSumFromJSON :: Value -> Result BigSum
145+thBigSumFromJSON = parse thBigSumParseJSON
146+
147+--------------------------------------------------------------------------------
148+
149+type FJ a = Value -> Result a
150+
151+main :: IO ()
152+main = defaultMain
153+ [ let v = thDToJSON d
154+ in d `deepseq` v `deepseq`
155+ bgroup "D"
156+ [ group "toJSON" (nf thDToJSON d)
157+ (nf G.toJSON d)
158+ (nf toJSON d)
159+ , group "fromJSON" (nf (thDFromJSON :: FJ T) v)
160+ (nf (G.fromJSON :: FJ T) v)
161+ (nf (fromJSON :: FJ T) v)
162+ ]
163+ , let v = thBigRecordToJSON bigRecord
164+ in bigRecord `deepseq` v `deepseq`
165+ bgroup "BigRecord"
166+ [ group "toJSON" (nf thBigRecordToJSON bigRecord)
167+ (nf G.toJSON bigRecord)
168+ (nf toJSON bigRecord)
169+ , group "fromJSON" (nf (thBigRecordFromJSON :: FJ BigRecord) v)
170+ (nf (G.fromJSON :: FJ BigRecord) v)
171+ (nf (fromJSON :: FJ BigRecord) v)
172+ ]
173+ , let v = thBigProductToJSON bigProduct
174+ in bigProduct `deepseq` v `deepseq`
175+ bgroup "BigProduct"
176+ [ group "toJSON" (nf thBigProductToJSON bigProduct)
177+ (nf G.toJSON bigProduct)
178+ (nf toJSON bigProduct)
179+ , group "fromJSON" (nf (thBigProductFromJSON :: FJ BigProduct) v)
180+ (nf (G.fromJSON :: FJ BigProduct) v)
181+ (nf (fromJSON :: FJ BigProduct) v)
182+ ]
183+ , let v = thBigSumToJSON bigSum
184+ in bigSum `deepseq` v `deepseq`
185+ bgroup "BigSum"
186+ [ group "toJSON" (nf thBigSumToJSON bigSum)
187+ (nf G.toJSON bigSum)
188+ (nf toJSON bigSum)
189+ , group "fromJSON" (nf (thBigSumFromJSON :: FJ BigSum) v)
190+ (nf (G.fromJSON :: FJ BigSum) v)
191+ (nf (fromJSON :: FJ BigSum) v)
192+ ]
193+ ]
194+
195+group n th syb gen = bgroup n [ bench "th" th
196+ , bench "syb" syb
197+ , bench "generic" gen
198+ ]
benchmarks/AesonTuples.hsnew file+45
@@ -0,0 +1,45 @@
1+module Main where
2+
3+--------------------------------------------------------------------------------
4+
5+import Criterion.Main
6+import Control.DeepSeq (deepseq)
7+import Data.Aeson
8+
9+--------------------------------------------------------------------------------
10+
11+type FJ a = Value -> Result a
12+
13+type T2 = (Int, Int)
14+type T3 = (Int, Int, Int)
15+type T4 = (Int, Int, Int, Int)
16+
17+t2 :: T2
18+t2 = (1, 2)
19+
20+t3 :: T3
21+t3 = (1, 2, 3)
22+
23+t4 :: T4
24+t4 = (1, 2, 3, 4)
25+
26+main :: IO ()
27+main = let v2 = toJSON t2
28+ v3 = toJSON t3
29+ v4 = toJSON t4
30+ in t2 `deepseq` t3 `deepseq` t4 `deepseq`
31+ v2 `deepseq` v3 `deepseq` v4 `deepseq`
32+ defaultMain
33+ [ bgroup "t2"
34+ [ bench "toJSON" (nf toJSON t2)
35+ , bench "fromJSON" (nf (fromJSON :: FJ T2) v2)
36+ ]
37+ , bgroup "t3"
38+ [ bench "toJSON" (nf toJSON t3)
39+ , bench "fromJSON" (nf (fromJSON :: FJ T3) v3)
40+ ]
41+ , bgroup "t4"
42+ [ bench "toJSON" (nf toJSON t4)
43+ , bench "fromJSON" (nf (fromJSON :: FJ T4) v4)
44+ ]
45+ ]
benchmarks/Makefile+1−1
@@ -1,7 +1,7 @@
11ghc := ghc
22ghcflags := -O
33
4−binaries := AesonParse AesonEncode JsonParse
4+binaries := AesonParse AesonEncode JsonParse AesonCompareAutoInstances
55
66all: $(binaries) $(binaries:%=%_p)
77
tests/Makefiledeleted−9
@@ -1,9 +0,0 @@
1−ghc := ghc
2−
3−all: qc
4−
5−qc: Properties.hs
6− $(ghc) -Wall -o -threaded --make $@ $<
7−
8−clean:
9− -rm -f qc *.o *.hi
tests/Properties.hs+101−15
@@ -1,36 +1,47 @@
1−{-# LANGUAGE OverloadedStrings, ScopedTypeVariables #-}
1+{-# LANGUAGE DeriveDataTypeable, OverloadedStrings, RecordWildCards,
2+ ScopedTypeVariables #-}
3+{-# OPTIONS_GHC -fno-warn-orphans #-}
24
5+import Control.Monad
6+import Control.Applicative
37import Data.Aeson.Encode
48import Data.Aeson.Parser (value)
59import Data.Aeson.Types
610import Data.Attoparsec.Number
11+import Data.Data (Typeable, Data)
12+import Data.Text (Text)
713import Test.Framework (Test, defaultMain, testGroup)
814import Test.Framework.Providers.QuickCheck2 (testProperty)
9−import Test.QuickCheck (Arbitrary)
10−import qualified Data.ByteString.Lazy.Char8 as L
15+import Test.QuickCheck (Arbitrary(..))
16+import qualified Data.Aeson.Generic as G
1117import qualified Data.Attoparsec.Lazy as L
18+import qualified Data.ByteString.Lazy.Char8 as L
19+import qualified Data.Text as T
20+import qualified Data.Map as Map
1221
1322encodeDouble :: Double -> Double -> Bool
1423encodeDouble num denom
1524 | isInfinite d || isNaN d = encode (Number (D d)) == "null"
16− | otherwise = encode (Number (D d)) == L.pack (show d)
25+ | otherwise = (read . L.unpack . encode . Number . D) d == d
1726 where d = num / denom
1827
1928encodeInteger :: Integer -> Bool
2029encodeInteger i = encode (Number (I i)) == L.pack (show i)
2130
22−roundTrip :: (FromJSON a, ToJSON a) => (a -> a -> Bool) -> a -> Bool
23−roundTrip eq i =
31+roundTrip :: (FromJSON a, ToJSON a) => (a -> a -> Bool) -> a -> a -> Bool
32+roundTrip eq _ i =
2433 case fmap fromJSON . L.parse value . encode . toJSON $ i of
2534 L.Done _ (Success v) -> v `eq` i
2635 _ -> False
2736
28−roundTripBool :: Bool -> Bool
29−roundTripBool = roundTrip (==)
30−roundTripDouble :: Double -> Bool
31−roundTripDouble = roundTrip approxEq
32−roundTripInteger :: Integer -> Bool
33−roundTripInteger = roundTrip (==)
37+roundTripEq :: (Eq a, FromJSON a, ToJSON a) => a -> a -> Bool
38+roundTripEq x y = roundTrip (==) x y
39+
40+genericTo :: (Data a, ToJSON a) => a -> a -> Bool
41+genericTo _ v = G.toJSON v == toJSON v
42+
43+genericFrom :: (Eq a, Data a, ToJSON a) => a -> a -> Bool
44+genericFrom _ v = G.fromJSON (toJSON v) == Success v
3445
3546approxEq :: Double -> Double -> Bool
3647approxEq a b = a == b ||
@@ -45,6 +56,62 @@ toFromJSON x = case fromJSON . toJSON $ x of
4556 Error _ -> False
4657 Success x' -> x == x'
4758
59+genericToFromJSON :: (Arbitrary a, Eq a, Data a) => a -> Bool
60+genericToFromJSON x = case G.fromJSON . G.toJSON $ x of
61+ Error _ -> False
62+ Success x' -> x == x'
63+
64+
65+data Foo = Foo {
66+ fooInt :: Int
67+ , fooDouble :: Double
68+ , fooTuple :: (String, Text, Int)
69+ -- This definition causes an infinite loop in genericTo and genericFrom!
70+ -- , fooMap :: Map.Map String Foo
71+ , fooMap :: Map.Map String (Text,Int)
72+ } deriving (Show, Typeable, Data)
73+
74+instance Eq Foo where
75+ a == b = fooInt a == fooInt b &&
76+ fooDouble a `approxEq` fooDouble b &&
77+ fooTuple a == fooTuple b
78+
79+instance ToJSON Foo where
80+ toJSON Foo{..} = object [ "fooInt" .= fooInt
81+ , "fooDouble" .= fooDouble
82+ , "fooTuple" .= fooTuple
83+ , "fooMap" .= fooMap
84+ ]
85+
86+instance FromJSON Foo where
87+ parseJSON (Object v) = Foo <$>
88+ v .: "fooInt" <*>
89+ v .: "fooDouble" <*>
90+ v .: "fooTuple" <*>
91+ v .: "fooMap"
92+ parseJSON _ = empty
93+
94+instance Arbitrary Text where
95+ arbitrary = T.pack <$> arbitrary
96+
97+instance (Ord k, Arbitrary k, Arbitrary v) => Arbitrary (Map.Map k v) where
98+ arbitrary = Map.fromList <$> arbitrary
99+
100+instance Arbitrary Foo where
101+ arbitrary = liftM4 Foo arbitrary arbitrary arbitrary arbitrary
102+
103+{-
104+ Test for Data.Aeson.Generic handling '_' names
105+-}
106+data UFoo = UFoo {
107+ _UFooInt :: Int
108+ , uFooInt :: Int
109+ } deriving (Show, Eq, Data, Typeable)
110+
111+instance Arbitrary UFoo where
112+ arbitrary = UFoo <$> arbitrary <*> arbitrary
113+ where _ = uFooInt
114+
48115main :: IO ()
49116main = defaultMain tests
50117
@@ -54,10 +121,26 @@ tests = [
54121 testProperty "encodeDouble" encodeDouble
55122 , testProperty "encodeInteger" encodeInteger
56123 ],
124+ testGroup "genericFrom" [
125+ testProperty "Bool" $ genericFrom True
126+ , testProperty "Double" $ genericFrom (1::Double)
127+ , testProperty "Int" $ genericFrom (1::Int)
128+ , testProperty "Foo" $ genericFrom (undefined::Foo)
129+ ],
130+ testGroup "genericTo" [
131+ testProperty "Bool" $ genericTo True
132+ , testProperty "Double" $ genericTo (1::Double)
133+ , testProperty "Int" $ genericTo (1::Int)
134+ , testProperty "Foo" $ genericTo (undefined::Foo)
135+ ],
57136 testGroup "roundTrip" [
58− testProperty "roundTripBool" roundTripBool
59− , testProperty "roundTripDouble" roundTripDouble
60− , testProperty "roundTripInteger" roundTripInteger
137+ testProperty "Bool" $ roundTripEq True
138+ , testProperty "Double" $ roundTrip approxEq (1::Double)
139+ , testProperty "Int" $ roundTripEq (1::Int)
140+ , testProperty "Integer" $ roundTripEq (1::Integer)
141+ , testProperty "String" $ roundTripEq (""::String)
142+ , testProperty "Text" $ roundTripEq T.empty
143+ , testProperty "Foo" $ roundTripEq (undefined::Foo)
61144 ],
62145 testGroup "toFromJSON" [
63146 testProperty "Integer" (toFromJSON :: Integer -> Bool)
@@ -65,5 +148,8 @@ tests = [
65148 , testProperty "Maybe Integer" (toFromJSON :: Maybe Integer -> Bool)
66149 , testProperty "Either Integer Double" (toFromJSON :: Either Integer Double -> Bool)
67150 , testProperty "Either Integer Integer" (toFromJSON :: Either Integer Integer -> Bool)
151+ ],
152+ testGroup "genericToFromJSON" [
153+ testProperty "_UFoo" (genericToFromJSON :: UFoo -> Bool)
68154 ]
69155 ]