on master help sign in
Merge branch 'master' of git://github.com/mailrank/aeson into optimizations

Also removed some unnecessary language extensions Conflicts: Data/Aeson/Types/Internal.hs

commitfa9c2abe89e1a272ff414f9e67913df3b6572143 authorBas van Dijk <v.dijk.bas@gmail.com> dateFri 4 Nov 2011, 10:21 (15 years ago) parent3ea27d6be81de8
6 files changed+1037−961
Data/Aeson/Generic.hs+1−1 Data/Aeson/Types.hs+7 Data/Aeson/Types/Class.hs+732 Data/Aeson/Types/Generic.hs+285 Data/Aeson/Types/Internal.hs+5−956 aeson.cabal+7−4
Data/Aeson/Generic.hs+1−1
@@ -1,4 +1,4 @@
1−{-# LANGUAGE PatternGuards, RankNTypes, ScopedTypeVariables #-}
1+{-# LANGUAGE PatternGuards, Rank2Types, ScopedTypeVariables #-}
22
33-- |
44-- Module: Data.Aeson.Generic
Data/Aeson/Types.hs+7
@@ -1,3 +1,5 @@
1+{-# LANGUAGE CPP #-}
2+
13-- |
24-- Module: Data.Aeson.Types
35-- Copyright: (c) 2011 MailRank, Inc.
@@ -36,4 +38,9 @@ module Data.Aeson.Types
3638 , object
3739 ) where
3840
41+import Data.Aeson.Types.Class
3942import Data.Aeson.Types.Internal
43+
44+#ifdef GENERICS
45+import Data.Aeson.Types.Generic ()
46+#endif
Data/Aeson/Types/Class.hsnew file+732
@@ -0,0 +1,732 @@
1+{-# LANGUAGE CPP
2+ , DeriveDataTypeable
3+ , FlexibleContexts
4+ , FlexibleInstances
5+ , GeneralizedNewtypeDeriving
6+ , OverlappingInstances
7+ , OverloadedStrings
8+ , UndecidableInstances
9+ , ViewPatterns
10+ #-}
11+
12+#ifdef GENERICS
13+{-# LANGUAGE DefaultSignatures #-}
14+#endif
15+
16+-- |
17+-- Module: Data.Aeson.Types.Class
18+-- Copyright: (c) 2011 MailRank, Inc.
19+-- License: Apache
20+-- Maintainer: Bryan O'Sullivan <bos@mailrank.com>
21+-- Stability: experimental
22+-- Portability: portable
23+--
24+-- Types for working with JSON data.
25+
26+module Data.Aeson.Types.Class
27+ (
28+ -- * Type classes
29+ -- ** Core JSON classes
30+ FromJSON(..)
31+ , ToJSON(..)
32+#ifdef GENERICS
33+ -- ** Generic JSON classes
34+ , GFromJSON(..)
35+ , GToJSON(..)
36+#endif
37+ -- * Types
38+ , DotNetTime(..)
39+ -- * Functions
40+ , fromJSON
41+ , (.:)
42+ , (.:?)
43+ , (.=)
44+ , typeMismatch
45+ ) where
46+
47+import Control.Applicative ((<$>), (<*>), pure)
48+import Data.Aeson.Functions
49+import Data.Aeson.Types.Internal
50+import Data.Attoparsec.Char8 (Number(..))
51+import Data.Hashable (Hashable(..))
52+import Data.Int (Int8, Int16, Int32, Int64)
53+import Data.Monoid (Dual(..), First(..), Last(..))
54+import Data.Ratio (Ratio)
55+import Data.Text (Text, pack, unpack)
56+import Data.Text.Encoding (encodeUtf8)
57+import Data.Time.Clock (UTCTime)
58+import Data.Time.Format (FormatTime, formatTime, parseTime)
59+import Data.Typeable (Typeable)
60+import Data.Vector (Vector)
61+import Data.Word (Word, Word8, Word16, Word32, Word64)
62+import Foreign.Storable (Storable)
63+import System.Locale (defaultTimeLocale)
64+import qualified Data.ByteString as B
65+import qualified Data.ByteString.Lazy as LB
66+import qualified Data.HashMap.Strict as H
67+import qualified Data.HashSet as HashSet
68+import qualified Data.IntMap as IntMap
69+import qualified Data.IntSet as IntSet
70+import qualified Data.Map as M
71+import qualified Data.Set as Set
72+import qualified Data.Text as T
73+import qualified Data.Text.Lazy as LT
74+import qualified Data.Vector as V
75+import qualified Data.Vector.Generic as VG
76+import qualified Data.Vector.Primitive as VP
77+import qualified Data.Vector.Storable as VS
78+import qualified Data.Vector.Unboxed as VU
79+
80+#ifdef GENERICS
81+import GHC.Generics
82+
83+class GToJSON f where
84+ gToJSON :: f a -> Value
85+
86+class GFromJSON f where
87+ gParseJSON :: Value -> Parser (f a)
88+#endif
89+
90+-- | A type that can be converted to JSON.
91+--
92+-- An example type and instance:
93+--
94+-- @{-\# LANGUAGE OverloadedStrings #-}
95+--
96+-- data Coord { x :: Double, y :: Double }
97+--
98+-- instance ToJSON Coord where
99+-- toJSON (Coord x y) = 'object' [\"x\" '.=' x, \"y\" '.=' y]
100+-- @
101+--
102+-- Note the use of the @OverloadedStrings@ language extension which enables
103+-- 'Text' values to be written as string literals.
104+--
105+-- Instead of manually writing your 'ToJSON' instance, there are three options
106+-- to do it automatically:
107+--
108+-- * 'Data.Aeson.TH' provides template-haskell functions which will derive an
109+-- instance at compile-time. The generated instance is optimized for your type
110+-- so will probably be more efficient than the following two options:
111+--
112+-- * 'Data.Aeson.Generic' provides a generic @toJSON@ function that accepts any
113+-- type which is an instance of 'Data'.
114+--
115+-- * If your compiler has support for the @DeriveGeneric@ and
116+-- @DefaultSignatures@ language extensions, @toJSON@ will have a default generic
117+-- implementation.
118+--
119+-- To use the latter option, simply add a @deriving 'Generic'@ clause to your
120+-- datatype and declare a @ToJSON@ instance for your datatype without giving a
121+-- definition for @toJSON@.
122+--
123+-- For example the previous example can be simplified to just:
124+--
125+-- @{-\# LANGUAGE DeriveGeneric \#-}
126+--
127+-- import GHC.Generics
128+--
129+-- data Coord { x :: Double, y :: Double } deriving Generic
130+--
131+-- instance ToJSON Coord
132+-- @
133+class ToJSON a where
134+ toJSON :: a -> Value
135+
136+#ifdef GENERICS
137+ default toJSON :: (Generic a, GToJSON (Rep a)) => a -> Value
138+ toJSON = gToJSON . from
139+#endif
140+
141+-- | A type that can be converted from JSON, with the possibility of
142+-- failure.
143+--
144+-- When writing an instance, use 'empty', 'mzero', or 'fail' to make a
145+-- conversion fail, e.g. if an 'Object' is missing a required key, or
146+-- the value is of the wrong type.
147+--
148+-- An example type and instance:
149+--
150+-- @{-\# LANGUAGE OverloadedStrings #-}
151+--
152+-- data Coord { x :: Double, y :: Double }
153+--
154+-- instance FromJSON Coord where
155+-- parseJSON ('Object' v) = Coord '<$>'
156+-- v '.:' \"x\" '<*>'
157+-- v '.:' \"y\"
158+--
159+-- \-- A non-'Object' value is of the wrong type, so use 'mzero' to fail.
160+-- parseJSON _ = 'mzero'
161+-- @
162+--
163+-- Note the use of the @OverloadedStrings@ language extension which enables
164+-- 'Text' values to be written as string literals.
165+--
166+-- Instead of manually writing your 'FromJSON' instance, there are three options
167+-- to do it automatically:
168+--
169+-- * 'Data.Aeson.TH' provides template-haskell functions which will derive an
170+-- instance at compile-time. The generated instance is optimized for your type
171+-- so will probably be more efficient than the following two options:
172+--
173+-- * 'Data.Aeson.Generic' provides a generic @fromJSON@ function that parses to
174+-- any type which is an instance of 'Data'.
175+--
176+-- * If your compiler has support for the @DeriveGeneric@ and
177+-- @DefaultSignatures@ language extensions, @parseJSON@ will have a default
178+-- generic implementation.
179+--
180+-- To use this, simply add a @deriving 'Generic'@ clause to your datatype and
181+-- declare a @FromJSON@ instance for your datatype without giving a definition
182+-- for @parseJSON@.
183+--
184+-- For example the previous example can be simplified to just:
185+--
186+-- @{-\# LANGUAGE DeriveGeneric \#-}
187+--
188+-- import GHC.Generics
189+--
190+-- data Coord { x :: Double, y :: Double } deriving Generic
191+--
192+-- instance FromJSON Coord
193+-- @
194+class FromJSON a where
195+ parseJSON :: Value -> Parser a
196+
197+#ifdef GENERICS
198+ default parseJSON :: (Generic a, GFromJSON (Rep a)) => Value -> Parser a
199+ parseJSON = fmap to . gParseJSON
200+#endif
201+
202+instance (ToJSON a) => ToJSON (Maybe a) where
203+ toJSON (Just a) = toJSON a
204+ toJSON Nothing = Null
205+ {-# INLINE toJSON #-}
206+
207+instance (FromJSON a) => FromJSON (Maybe a) where
208+ parseJSON Null = pure Nothing
209+ parseJSON a = Just <$> parseJSON a
210+ {-# INLINE parseJSON #-}
211+
212+instance (ToJSON a, ToJSON b) => ToJSON (Either a b) where
213+ toJSON (Left a) = object [left .= a]
214+ toJSON (Right b) = object [right .= b]
215+ {-# INLINE toJSON #-}
216+
217+instance (FromJSON a, FromJSON b) => FromJSON (Either a b) where
218+ parseJSON (Object (M.toList -> [(key, value)]))
219+ | key == left = Left <$> parseJSON value
220+ | key == right = Right <$> parseJSON value
221+ parseJSON _ = fail ""
222+ {-# INLINE parseJSON #-}
223+
224+left, right :: Text
225+left = "Left"
226+right = "Right"
227+
228+instance ToJSON Bool where
229+ toJSON = Bool
230+ {-# INLINE toJSON #-}
231+
232+instance FromJSON Bool where
233+ parseJSON (Bool b) = pure b
234+ parseJSON v = typeMismatch "Bool" v
235+ {-# INLINE parseJSON #-}
236+
237+instance ToJSON () where
238+ toJSON _ = emptyArray
239+ {-# INLINE toJSON #-}
240+
241+instance FromJSON () where
242+ parseJSON (Array v) | V.null v = pure ()
243+ parseJSON v = typeMismatch "()" v
244+ {-# INLINE parseJSON #-}
245+
246+instance ToJSON [Char] where
247+ toJSON = String . T.pack
248+ {-# INLINE toJSON #-}
249+
250+instance FromJSON [Char] where
251+ parseJSON (String t) = pure (T.unpack t)
252+ parseJSON v = typeMismatch "String" v
253+ {-# INLINE parseJSON #-}
254+
255+instance ToJSON Char where
256+ toJSON = String . T.singleton
257+ {-# INLINE toJSON #-}
258+
259+instance FromJSON Char where
260+ parseJSON (String t)
261+ | T.compareLength t 1 == EQ = pure (T.head t)
262+ parseJSON v = typeMismatch "Char" v
263+ {-# INLINE parseJSON #-}
264+
265+instance ToJSON Double where
266+ toJSON = Number . D
267+ {-# INLINE toJSON #-}
268+
269+instance FromJSON Double where
270+ parseJSON (Number n) = case n of
271+ D d -> pure d
272+ I i -> pure (fromIntegral i)
273+ parseJSON Null = pure (0/0)
274+ parseJSON v = typeMismatch "Double" v
275+ {-# INLINE parseJSON #-}
276+
277+instance ToJSON Number where
278+ toJSON = Number
279+ {-# INLINE toJSON #-}
280+
281+instance FromJSON Number where
282+ parseJSON (Number n) = pure n
283+ parseJSON Null = pure (D (0/0))
284+ parseJSON v = typeMismatch "Number" v
285+ {-# INLINE parseJSON #-}
286+
287+instance ToJSON Float where
288+ toJSON = Number . realToFrac
289+ {-# INLINE toJSON #-}
290+
291+instance FromJSON Float where
292+ parseJSON (Number n) = pure $ case n of
293+ D d -> realToFrac d
294+ I i -> fromIntegral i
295+ parseJSON Null = pure (0/0)
296+ parseJSON v = typeMismatch "Float" v
297+ {-# INLINE parseJSON #-}
298+
299+instance ToJSON (Ratio Integer) where
300+ toJSON = Number . fromRational
301+ {-# INLINE toJSON #-}
302+
303+instance FromJSON (Ratio Integer) where
304+ parseJSON (Number n) = pure $ case n of
305+ D d -> toRational d
306+ I i -> fromIntegral i
307+ parseJSON v = typeMismatch "Ratio Integer" v
308+ {-# INLINE parseJSON #-}
309+
310+instance ToJSON Int where
311+ toJSON = Number . fromIntegral
312+ {-# INLINE toJSON #-}
313+
314+instance FromJSON Int where
315+ parseJSON = parseIntegral
316+ {-# INLINE parseJSON #-}
317+
318+parseIntegral :: Integral a => Value -> Parser a
319+parseIntegral (Number n) = pure (floor n)
320+parseIntegral v = typeMismatch "Integral" v
321+{-# INLINE parseIntegral #-}
322+
323+instance ToJSON Integer where
324+ toJSON = Number . fromIntegral
325+ {-# INLINE toJSON #-}
326+
327+instance FromJSON Integer where
328+ parseJSON = parseIntegral
329+ {-# INLINE parseJSON #-}
330+
331+instance ToJSON Int8 where
332+ toJSON = Number . fromIntegral
333+ {-# INLINE toJSON #-}
334+
335+instance FromJSON Int8 where
336+ parseJSON = parseIntegral
337+ {-# INLINE parseJSON #-}
338+
339+instance ToJSON Int16 where
340+ toJSON = Number . fromIntegral
341+ {-# INLINE toJSON #-}
342+
343+instance FromJSON Int16 where
344+ parseJSON = parseIntegral
345+ {-# INLINE parseJSON #-}
346+
347+instance ToJSON Int32 where
348+ toJSON = Number . fromIntegral
349+ {-# INLINE toJSON #-}
350+
351+instance FromJSON Int32 where
352+ parseJSON = parseIntegral
353+ {-# INLINE parseJSON #-}
354+
355+instance ToJSON Int64 where
356+ toJSON = Number . fromIntegral
357+ {-# INLINE toJSON #-}
358+
359+instance FromJSON Int64 where
360+ parseJSON = parseIntegral
361+ {-# INLINE parseJSON #-}
362+
363+instance ToJSON Word where
364+ toJSON = Number . fromIntegral
365+ {-# INLINE toJSON #-}
366+
367+instance FromJSON Word where
368+ parseJSON = parseIntegral
369+ {-# INLINE parseJSON #-}
370+
371+instance ToJSON Word8 where
372+ toJSON = Number . fromIntegral
373+ {-# INLINE toJSON #-}
374+
375+instance FromJSON Word8 where
376+ parseJSON = parseIntegral
377+ {-# INLINE parseJSON #-}
378+
379+instance ToJSON Word16 where
380+ toJSON = Number . fromIntegral
381+ {-# INLINE toJSON #-}
382+
383+instance FromJSON Word16 where
384+ parseJSON = parseIntegral
385+ {-# INLINE parseJSON #-}
386+
387+instance ToJSON Word32 where
388+ toJSON = Number . fromIntegral
389+ {-# INLINE toJSON #-}
390+
391+instance FromJSON Word32 where
392+ parseJSON = parseIntegral
393+ {-# INLINE parseJSON #-}
394+
395+instance ToJSON Word64 where
396+ toJSON = Number . fromIntegral
397+ {-# INLINE toJSON #-}
398+
399+instance FromJSON Word64 where
400+ parseJSON = parseIntegral
401+ {-# INLINE parseJSON #-}
402+
403+instance ToJSON Text where
404+ toJSON = String
405+ {-# INLINE toJSON #-}
406+
407+instance FromJSON Text where
408+ parseJSON (String t) = pure t
409+ parseJSON v = typeMismatch "Text" v
410+ {-# INLINE parseJSON #-}
411+
412+instance ToJSON LT.Text where
413+ toJSON = String . LT.toStrict
414+ {-# INLINE toJSON #-}
415+
416+instance FromJSON LT.Text where
417+ parseJSON (String t) = pure (LT.fromStrict t)
418+ parseJSON v = typeMismatch "Lazy Text" v
419+ {-# INLINE parseJSON #-}
420+
421+instance ToJSON B.ByteString where
422+ toJSON = String . decode
423+ {-# INLINE toJSON #-}
424+
425+instance FromJSON B.ByteString where
426+ parseJSON (String t) = pure . encodeUtf8 $ t
427+ parseJSON v = typeMismatch "ByteString" v
428+ {-# INLINE parseJSON #-}
429+
430+instance ToJSON LB.ByteString where
431+ toJSON = toJSON . strict
432+ {-# INLINE toJSON #-}
433+
434+instance FromJSON LB.ByteString where
435+ parseJSON (String t) = pure . lazy $ t
436+ parseJSON v = typeMismatch "Lazy ByteString" v
437+ {-# INLINE parseJSON #-}
438+
439+instance (ToJSON a) => ToJSON [a] where
440+ toJSON = Array . V.fromList . map toJSON
441+ {-# INLINE toJSON #-}
442+
443+instance (FromJSON a) => FromJSON [a] where
444+ parseJSON (Array a) = mapM parseJSON (V.toList a)
445+ parseJSON v = typeMismatch "[a]" v
446+ {-# INLINE parseJSON #-}
447+
448+instance (ToJSON a) => ToJSON (Vector a) where
449+ toJSON = Array . V.map toJSON
450+ {-# INLINE toJSON #-}
451+
452+instance (FromJSON a) => FromJSON (Vector a) where
453+ parseJSON (Array a) = V.mapM parseJSON a
454+ parseJSON v = typeMismatch "Vector a" v
455+ {-# INLINE parseJSON #-}
456+
457+vectorToJSON :: (VG.Vector v a, ToJSON a) => v a -> Value
458+vectorToJSON = Array . V.map toJSON . V.convert
459+{-# INLINE vectorToJSON #-}
460+
461+vectorParseJSON :: (FromJSON a, VG.Vector w a) => String -> Value -> Parser (w a)
462+vectorParseJSON _ (Array a) = V.convert <$> V.mapM parseJSON a
463+vectorParseJSON s v = typeMismatch s v
464+{-# INLINE vectorParseJSON #-}
465+
466+instance (Storable a, ToJSON a) => ToJSON (VS.Vector a) where
467+ toJSON = vectorToJSON
468+
469+instance (Storable a, FromJSON a) => FromJSON (VS.Vector a) where
470+ parseJSON = vectorParseJSON "Data.Vector.Storable.Vector a"
471+
472+instance (VP.Prim a, ToJSON a) => ToJSON (VP.Vector a) where
473+ toJSON = vectorToJSON
474+
475+instance (VP.Prim a, FromJSON a) => FromJSON (VP.Vector a) where
476+ parseJSON = vectorParseJSON "Data.Vector.Primitive.Vector a"
477+
478+instance (VG.Vector VU.Vector a, ToJSON a) => ToJSON (VU.Vector a) where
479+ toJSON = vectorToJSON
480+
481+instance (VG.Vector VU.Vector a, FromJSON a) => FromJSON (VU.Vector a) where
482+ parseJSON = vectorParseJSON "Data.Vector.Unboxed.Vector a"
483+
484+instance (ToJSON a) => ToJSON (Set.Set a) where
485+ toJSON = toJSON . Set.toList
486+ {-# INLINE toJSON #-}
487+
488+instance (Ord a, FromJSON a) => FromJSON (Set.Set a) where
489+ parseJSON = fmap Set.fromList . parseJSON
490+ {-# INLINE parseJSON #-}
491+
492+instance (ToJSON a) => ToJSON (HashSet.HashSet a) where
493+ toJSON = toJSON . HashSet.toList
494+ {-# INLINE toJSON #-}
495+
496+instance (Eq a, Hashable a, FromJSON a) => FromJSON (HashSet.HashSet a) where
497+ parseJSON = fmap HashSet.fromList . parseJSON
498+ {-# INLINE parseJSON #-}
499+
500+instance ToJSON IntSet.IntSet where
501+ toJSON = toJSON . IntSet.toList
502+ {-# INLINE toJSON #-}
503+
504+instance FromJSON IntSet.IntSet where
505+ parseJSON = fmap IntSet.fromList . parseJSON
506+ {-# INLINE parseJSON #-}
507+
508+instance ToJSON a => ToJSON (IntMap.IntMap a) where
509+ toJSON = toJSON . IntMap.toList
510+ {-# INLINE toJSON #-}
511+
512+instance FromJSON a => FromJSON (IntMap.IntMap a) where
513+ parseJSON = fmap IntMap.fromList . parseJSON
514+ {-# INLINE parseJSON #-}
515+
516+instance (ToJSON v) => ToJSON (M.Map Text v) where
517+ toJSON = Object . M.map toJSON
518+ {-# INLINE toJSON #-}
519+
520+instance (FromJSON v) => FromJSON (M.Map Text v) where
521+ parseJSON (Object o) = M.fromAscList <$> mapM go (M.toAscList o)
522+ where go (k,v) = ((,) k) <$> parseJSON v
523+ parseJSON v = typeMismatch "Map Text a" v
524+
525+instance (ToJSON v) => ToJSON (M.Map LT.Text v) where
526+ toJSON = Object . transformMap LT.toStrict toJSON
527+
528+instance (FromJSON v) => FromJSON (M.Map LT.Text v) where
529+ parseJSON = fmap (M.mapKeysMonotonic LT.fromStrict) . parseJSON
530+
531+instance (ToJSON v) => ToJSON (M.Map String v) where
532+ toJSON = Object . transformMap pack toJSON
533+
534+instance (FromJSON v) => FromJSON (M.Map String v) where
535+ parseJSON = fmap (M.mapKeysMonotonic unpack) . parseJSON
536+
537+instance (ToJSON v) => ToJSON (M.Map B.ByteString v) where
538+ toJSON = Object . transformMap decode toJSON
539+
540+instance (FromJSON v) => FromJSON (M.Map B.ByteString v) where
541+ parseJSON = fmap (M.mapKeysMonotonic encodeUtf8) . parseJSON
542+
543+instance (ToJSON v) => ToJSON (M.Map LB.ByteString v) where
544+ toJSON = Object . transformMap strict toJSON
545+
546+instance (FromJSON v) => FromJSON (M.Map LB.ByteString v) where
547+ parseJSON = fmap (M.mapKeysMonotonic lazy) . parseJSON
548+
549+instance (ToJSON v) => ToJSON (H.HashMap Text v) where
550+ toJSON = Object . hashMap id toJSON
551+ {-# INLINE toJSON #-}
552+
553+instance (FromJSON v) => FromJSON (H.HashMap Text v) where
554+ parseJSON (Object o) = H.fromList <$> mapM go (M.toList o)
555+ where go (k,v) = ((,) k) <$> parseJSON v
556+ parseJSON v = typeMismatch "HashMap Text a" v
557+
558+instance (ToJSON v) => ToJSON (H.HashMap LT.Text v) where
559+ toJSON = Object . M.fromList . H.foldrWithKey (\k v -> ((LT.toStrict k,toJSON v) :)) []
560+
561+instance (FromJSON v) => FromJSON (H.HashMap LT.Text v) where
562+ parseJSON = fmap (mapHash LT.fromStrict) . parseJSON
563+
564+instance (ToJSON v) => ToJSON (H.HashMap String v) where
565+ toJSON = Object . hashMap pack toJSON
566+
567+instance (FromJSON v) => FromJSON (H.HashMap String v) where
568+ parseJSON = fmap (mapHash unpack) . parseJSON
569+
570+instance (ToJSON v) => ToJSON (H.HashMap B.ByteString v) where
571+ toJSON = Object . hashMap decode toJSON
572+
573+instance (FromJSON v) => FromJSON (H.HashMap B.ByteString v) where
574+ parseJSON = fmap (mapHash encodeUtf8) . parseJSON
575+
576+instance (ToJSON v) => ToJSON (H.HashMap LB.ByteString v) where
577+ toJSON = Object . hashMap strict toJSON
578+
579+instance (FromJSON v) => FromJSON (H.HashMap LB.ByteString v) where
580+ parseJSON = fmap (mapHash lazy) . parseJSON
581+
582+instance ToJSON Value where
583+ toJSON a = a
584+ {-# INLINE toJSON #-}
585+
586+instance FromJSON Value where
587+ parseJSON a = pure a
588+ {-# INLINE parseJSON #-}
589+
590+-- | A newtype wrapper for 'UTCTime' that uses the same non-standard
591+-- serialization format as Microsoft .NET, whose @System.DateTime@
592+-- type is by default serialized to JSON as in the following example:
593+--
594+-- > /Date(1302547608878)/
595+--
596+-- The number represents milliseconds since the Unix epoch.
597+newtype DotNetTime = DotNetTime {
598+ fromDotNetTime :: UTCTime
599+ } deriving (Eq, Ord, Read, Show, Typeable, FormatTime)
600+
601+instance ToJSON DotNetTime where
602+ toJSON (DotNetTime t) =
603+ String (pack (secs ++ msecs ++ ")/"))
604+ where secs = formatTime defaultTimeLocale "/Date(%s" t
605+ msecs = take 3 $ formatTime defaultTimeLocale "%q" t
606+ {-# INLINE toJSON #-}
607+
608+instance FromJSON DotNetTime where
609+ parseJSON (String t) =
610+ case parseTime defaultTimeLocale "/Date(%s%Q)/" (unpack t') of
611+ Just d -> pure (DotNetTime d)
612+ _ -> fail "could not parse .NET time"
613+ where (s,m) = T.splitAt (T.length t - 5) t
614+ t' = T.concat [s,".",m]
615+ parseJSON v = typeMismatch "DotNetTime" v
616+ {-# INLINE parseJSON #-}
617+
618+instance ToJSON UTCTime where
619+ toJSON t = String (pack (take 23 str ++ "Z"))
620+ where str = formatTime defaultTimeLocale "%FT%T%Q" t
621+ {-# INLINE toJSON #-}
622+
623+instance FromJSON UTCTime where
624+ parseJSON (String t) =
625+ case parseTime defaultTimeLocale "%FT%T%QZ" (unpack t) of
626+ Just d -> pure d
627+ _ -> fail "could not parse ISO-8601 date"
628+ parseJSON v = typeMismatch "UTCTime" v
629+ {-# INLINE parseJSON #-}
630+
631+instance (ToJSON a, ToJSON b) => ToJSON (a,b) where
632+ toJSON (a,b) = toJSON [toJSON a, toJSON b]
633+ {-# INLINE toJSON #-}
634+
635+instance (FromJSON a, FromJSON b) => FromJSON (a,b) where
636+ parseJSON (Array ab) =
637+ case V.toList ab of
638+ [a,b] -> (,) <$> parseJSON a <*> parseJSON b
639+ _ -> fail $ "cannot unpack array of length " ++
640+ show (V.length ab) ++ " into a pair"
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) = toJSON [toJSON a, toJSON b, toJSON c]
646+ {-# INLINE toJSON #-}
647+
648+instance (FromJSON a, FromJSON b, FromJSON c) => FromJSON (a,b,c) where
649+ parseJSON (Array abc) =
650+ case V.toList abc of
651+ [a,b,c] -> (,,) <$> parseJSON a <*> parseJSON b <*> parseJSON c
652+ _ -> fail $ "cannot unpack array of length " ++
653+ show (V.length abc) ++ " into a 3-tuple"
654+ parseJSON v = typeMismatch "(a,b,c)" v
655+ {-# INLINE parseJSON #-}
656+
657+instance ToJSON a => ToJSON (Dual a) where
658+ toJSON = toJSON . getDual
659+ {-# INLINE toJSON #-}
660+
661+instance FromJSON a => FromJSON (Dual a) where
662+ parseJSON = fmap Dual . parseJSON
663+ {-# INLINE parseJSON #-}
664+
665+instance ToJSON a => ToJSON (First a) where
666+ toJSON = toJSON . getFirst
667+ {-# INLINE toJSON #-}
668+
669+instance FromJSON a => FromJSON (First a) where
670+ parseJSON = fmap First . parseJSON
671+ {-# INLINE parseJSON #-}
672+
673+instance ToJSON a => ToJSON (Last a) where
674+ toJSON = toJSON . getLast
675+ {-# INLINE toJSON #-}
676+
677+instance FromJSON a => FromJSON (Last a) where
678+ parseJSON = fmap Last . parseJSON
679+ {-# INLINE parseJSON #-}
680+
681+-- | Construct a 'Pair' from a key and a value.
682+(.=) :: ToJSON a => Text -> a -> Pair
683+name .= value = (name, toJSON value)
684+{-# INLINE (.=) #-}
685+
686+-- | Convert a value from JSON, failing if the types do not match.
687+fromJSON :: (FromJSON a) => Value -> Result a
688+fromJSON = parse parseJSON
689+{-# INLINE fromJSON #-}
690+
691+-- | Retrieve the value associated with the given key of an 'Object'.
692+-- The result is 'empty' if the key is not present or the value cannot
693+-- be converted to the desired type.
694+--
695+-- This accessor is appropriate if the key and value /must/ be present
696+-- in an object for it to be valid. If the key and value are
697+-- optional, use '(.:?)' instead.
698+(.:) :: (FromJSON a) => Object -> Text -> Parser a
699+obj .: key = case M.lookup key obj of
700+ Nothing -> fail $ "key " ++ show key ++ " not present"
701+ Just v -> parseJSON v
702+{-# INLINE (.:) #-}
703+
704+-- | Retrieve the value associated with the given key of an 'Object'.
705+-- The result is 'Nothing' if the key is not present, or 'empty' if
706+-- the value cannot be converted to the desired type.
707+--
708+-- This accessor is most useful if the key and value can be absent
709+-- from an object without affecting its validity. If the key and
710+-- value are mandatory, use '(.:)' instead.
711+(.:?) :: (FromJSON a) => Object -> Text -> Parser (Maybe a)
712+obj .:? key = case M.lookup key obj of
713+ Nothing -> pure Nothing
714+ Just v -> parseJSON v
715+{-# INLINE (.:?) #-}
716+
717+
718+-- | Fail parsing due to a type mismatch, with a descriptive message.
719+typeMismatch :: String -- ^ The name of the type you are trying to parse.
720+ -> Value -- ^ The actual value encountered.
721+ -> Parser a
722+typeMismatch expected actual =
723+ fail $ "when expecting a " ++ expected ++ ", encountered " ++ name ++
724+ " instead"
725+ where
726+ name = case actual of
727+ Object _ -> "Object"
728+ Array _ -> "Array"
729+ String _ -> "String"
730+ Number _ -> "Number"
731+ Bool _ -> "Boolean"
732+ Null -> "Null"
Data/Aeson/Types/Generic.hsnew file+285
@@ -0,0 +1,285 @@
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@mailrank.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 GHC.Generics
34+import qualified Data.Map as M
35+import qualified Data.Text as T
36+import qualified Data.Vector as V
37+
38+--------------------------------------------------------------------------------
39+-- Generic toJSON
40+
41+instance (GToJSON a) => GToJSON (M1 i c a) where
42+ gToJSON = gToJSON . unM1
43+ {-# INLINE gToJSON #-}
44+
45+instance (ToJSON a) => GToJSON (K1 i a) where
46+ gToJSON = toJSON . unK1
47+ {-# INLINE gToJSON #-}
48+
49+instance GToJSON U1 where
50+ gToJSON _ = emptyArray
51+ {-# INLINE gToJSON #-}
52+
53+instance (ConsToJSON a) => GToJSON (C1 c a) where
54+ gToJSON = consToJSON . unM1
55+ {-# INLINE gToJSON #-}
56+
57+instance (GProductToValues a, GProductToValues b) => GToJSON (a :*: b) where
58+ gToJSON = toJSON . toList . gProductToValues
59+ {-# INLINE gToJSON #-}
60+
61+instance (GObject a, GObject b) => GToJSON (a :+: b) where
62+ gToJSON (L1 x) = Object $ gObject x
63+ gToJSON (R1 x) = Object $ gObject x
64+ {-# INLINE gToJSON #-}
65+
66+--------------------------------------------------------------------------------
67+
68+class ConsToJSON f where consToJSON :: f a -> Value
69+class ConsToJSON' b f where consToJSON' :: Tagged b (f a -> Value)
70+
71+newtype Tagged s b = Tagged {unTagged :: b}
72+
73+instance (IsRecord f b, ConsToJSON' b f) => ConsToJSON f where
74+ consToJSON = unTagged (consToJSON' :: Tagged b (f a -> Value))
75+ {-# INLINE consToJSON #-}
76+
77+instance (GRecordToPairs f) => ConsToJSON' True f where
78+ consToJSON' = Tagged (object . toList . gRecordToPairs)
79+ {-# INLINE consToJSON' #-}
80+
81+instance GToJSON f => ConsToJSON' False f where
82+ consToJSON' = Tagged gToJSON
83+ {-# INLINE consToJSON' #-}
84+
85+--------------------------------------------------------------------------------
86+
87+class GRecordToPairs f where
88+ gRecordToPairs :: f a -> DList Pair
89+
90+instance (GRecordToPairs a, GRecordToPairs b) => GRecordToPairs (a :*: b) where
91+ gRecordToPairs (a :*: b) = gRecordToPairs a `append` gRecordToPairs b
92+ {-# INLINE gRecordToPairs #-}
93+
94+instance (Selector s, GToJSON a) => GRecordToPairs (S1 s a) where
95+ gRecordToPairs m1 = singleton (pack (selName m1), gToJSON (unM1 m1))
96+ {-# INLINE gRecordToPairs #-}
97+
98+--------------------------------------------------------------------------------
99+
100+class GProductToValues f where
101+ gProductToValues :: f a -> DList Value
102+
103+instance (GProductToValues a, GProductToValues b) => GProductToValues (a :*: b) where
104+ gProductToValues (a :*: b) = gProductToValues a `append` gProductToValues b
105+ {-# INLINE gProductToValues #-}
106+
107+instance (GToJSON a) => GProductToValues a where
108+ gProductToValues = singleton . gToJSON
109+ {-# INLINE gProductToValues #-}
110+
111+--------------------------------------------------------------------------------
112+
113+class GObject f where
114+ gObject :: f a -> Object
115+
116+instance (GObject a, GObject b) => GObject (a :+: b) where
117+ gObject (L1 x) = gObject x
118+ gObject (R1 x) = gObject x
119+ {-# INLINE gObject #-}
120+
121+instance (Constructor c, GToJSON a, ConsToJSON a) => GObject (C1 c a) where
122+ gObject = M.singleton (pack $ conName (undefined :: t c a p)) . gToJSON
123+ {-# INLINE gObject #-}
124+
125+--------------------------------------------------------------------------------
126+-- Generic parseJSON
127+
128+instance (GFromJSON a) => GFromJSON (M1 i c a) where
129+ gParseJSON = fmap M1 . gParseJSON
130+ {-# INLINE gParseJSON #-}
131+
132+instance (FromJSON a) => GFromJSON (K1 i a) where
133+ gParseJSON = fmap K1 . parseJSON
134+ {-# INLINE gParseJSON #-}
135+
136+instance GFromJSON U1 where
137+ gParseJSON v
138+ | isEmptyArray v = pure U1
139+ | otherwise = typeMismatch "unit constructor (U1)" v
140+ {-# INLINE gParseJSON #-}
141+
142+instance (ConsFromJSON a) => GFromJSON (C1 c a) where
143+ gParseJSON = fmap M1 . consParseJSON
144+ {-# INLINE gParseJSON #-}
145+
146+instance ( GFromProduct a, GFromProduct b
147+ , ProductSize a, ProductSize b) => GFromJSON (a :*: b) where
148+ gParseJSON (Array arr)
149+ | lenArray == lenProduct = gParseProduct arr 0 lenProduct
150+ | otherwise =
151+ fail $ "When expecting a product of " ++ show lenProduct ++
152+ " values, encountered an Array of " ++ show lenArray ++
153+ " elements instead"
154+ where
155+ lenArray = V.length arr
156+ lenProduct = unTagged2 (productSize :: Tagged2 (a :*: b) Int)
157+
158+ gParseJSON v = typeMismatch "product (:*:)" v
159+ {-# INLINE gParseJSON #-}
160+
161+instance (GFromSum a, GFromSum b) => GFromJSON (a :+: b) where
162+ gParseJSON (Object (M.toList -> [keyVal@(key, _)])) =
163+ case gParseSum keyVal of
164+ Nothing -> notFound $ unpack key
165+ Just p -> p
166+ gParseJSON v = typeMismatch "sum (:+:)" v
167+ {-# INLINE gParseJSON #-}
168+
169+notFound :: String -> Parser a
170+notFound key = fail $ "The key \"" ++ key ++ "\" was not found"
171+{-# INLINE notFound #-}
172+
173+--------------------------------------------------------------------------------
174+
175+class ConsFromJSON f where consParseJSON :: Value -> Parser (f a)
176+class ConsFromJSON' b f where consParseJSON' :: Tagged b (Value -> Parser (f a))
177+
178+instance (IsRecord f b, ConsFromJSON' b f) => ConsFromJSON f where
179+ consParseJSON = unTagged (consParseJSON' :: Tagged b (Value -> Parser (f a)))
180+ {-# INLINE consParseJSON #-}
181+
182+instance (GFromRecord f) => ConsFromJSON' True f where
183+ consParseJSON' = Tagged parseRecord
184+ where
185+ parseRecord (Object obj) = gParseRecord obj
186+ parseRecord v = typeMismatch "record (:*:)" v
187+ {-# INLINE consParseJSON' #-}
188+
189+instance (GFromJSON f) => ConsFromJSON' False f where
190+ consParseJSON' = Tagged gParseJSON
191+ {-# INLINE consParseJSON' #-}
192+
193+--------------------------------------------------------------------------------
194+
195+class GFromRecord f where
196+ gParseRecord :: Object -> Parser (f a)
197+
198+instance (GFromRecord a, GFromRecord b) => GFromRecord (a :*: b) where
199+ gParseRecord obj = (:*:) <$> gParseRecord obj <*> gParseRecord obj
200+ {-# INLINE gParseRecord #-}
201+
202+instance (Selector s, GFromJSON a) => GFromRecord (S1 s a) where
203+ gParseRecord = maybe (notFound key) gParseJSON . M.lookup (T.pack key)
204+ where
205+ key = selName (undefined :: t s a p)
206+ {-# INLINE gParseRecord #-}
207+
208+--------------------------------------------------------------------------------
209+
210+class ProductSize f where
211+ productSize :: Tagged2 f Int
212+
213+newtype Tagged2 (s :: * -> *) b = Tagged2 {unTagged2 :: b}
214+
215+instance (ProductSize a, ProductSize b) => ProductSize (a :*: b) where
216+ productSize = Tagged2 $ unTagged2 (productSize :: Tagged2 a Int) +
217+ unTagged2 (productSize :: Tagged2 b Int)
218+
219+instance ProductSize (S1 s a) where
220+ productSize = Tagged2 1
221+
222+--------------------------------------------------------------------------------
223+
224+class GFromProduct f where
225+ gParseProduct :: Array -> Int -> Int -> Parser (f a)
226+
227+instance (GFromProduct a, GFromProduct b) => GFromProduct (a :*: b) where
228+ gParseProduct arr ix len = (:*:) <$> gParseProduct arr ix lenL
229+ <*> gParseProduct arr ixR lenR
230+ where
231+ lenL = len `shiftR` 1
232+ ixR = ix + lenL
233+ lenR = len - lenL
234+ {-# INLINE gParseProduct #-}
235+
236+instance (GFromJSON a) => GFromProduct (S1 s a) where
237+ gParseProduct arr ix _ = gParseJSON $ V.unsafeIndex arr ix
238+ {-# INLINE gParseProduct #-}
239+
240+--------------------------------------------------------------------------------
241+
242+class GFromSum f where
243+ gParseSum :: Pair -> Maybe (Parser (f a))
244+
245+instance (GFromSum a, GFromSum b) => GFromSum (a :+: b) where
246+ gParseSum keyVal = (fmap L1 <$> gParseSum keyVal) <|>
247+ (fmap R1 <$> gParseSum keyVal)
248+ {-# INLINE gParseSum #-}
249+
250+instance (Constructor c, GFromJSON a, ConsFromJSON a) => GFromSum (C1 c a) where
251+ gParseSum (key, value)
252+ | key == pack (conName (undefined :: t c a p)) = Just $ gParseJSON value
253+ | otherwise = Nothing
254+ {-# INLINE gParseSum #-}
255+
256+--------------------------------------------------------------------------------
257+
258+class IsRecord (f :: * -> *) b | f -> b
259+
260+data True
261+data False
262+
263+instance (IsRecord f b) => IsRecord (f :*: g) b
264+instance IsRecord (M1 S NoSelector f) False
265+instance (IsRecord f b) => IsRecord (M1 S c f) b
266+instance IsRecord (K1 i c) True
267+instance IsRecord U1 False
268+
269+--------------------------------------------------------------------------------
270+
271+type DList a = [a] -> [a]
272+
273+toList :: DList a -> [a]
274+toList = ($ [])
275+{-# INLINE toList #-}
276+
277+singleton :: a -> DList a
278+singleton = (:)
279+{-# INLINE singleton #-}
280+
281+append :: DList a -> DList a -> DList a
282+append = (.)
283+{-# INLINE append #-}
284+
285+--------------------------------------------------------------------------------
Data/Aeson/Types/Internal.hs+5−956
@@ -1,18 +1,4 @@
1−{-# LANGUAGE DeriveDataTypeable, FlexibleInstances, GeneralizedNewtypeDeriving,
2− IncoherentInstances, OverlappingInstances, OverloadedStrings, Rank2Types,
3− ViewPatterns, FlexibleContexts, UndecidableInstances,
4− ScopedTypeVariables, PatternGuards #-}
5−
6−{-# LANGUAGE CPP #-}
7−#ifdef GENERICS
8−{-# LANGUAGE DefaultSignatures
9− , TypeOperators
10− , EmptyDataDecls
11− , KindSignatures
12− , MultiParamTypeClasses
13− , FunctionalDependencies
14− #-}
15−#endif
1+{-# LANGUAGE CPP, DeriveDataTypeable, Rank2Types #-}
162
173-- |
184-- Module: Data.Aeson.Types.Internal
@@ -33,68 +19,31 @@ module Data.Aeson.Types.Internal
3319 , Pair
3420 , Object
3521 , emptyObject
36− -- * Convenience types and functions
37− , DotNetTime(..)
38− , typeMismatch
3922 -- * Type conversion
4023 , Parser
4124 , Result(..)
42− , FromJSON(..)
43− , fromJSON
4425 , parse
4526 , parseEither
4627 , parseMaybe
47− , ToJSON(..)
4828 -- * Constructors and accessors
49− , (.=)
50− , (.:)
51− , (.:?)
5229 , object
5330 ) where
5431
5532import Control.Applicative
5633import Control.DeepSeq (NFData(..))
5734import Control.Monad.State.Strict
58−import Data.Aeson.Functions
5935import Data.Attoparsec.Char8 (Number(..))
60−import Data.Bits (shiftR)
6136import Data.Data (Data)
6237import Data.Hashable (Hashable(..))
63−import Data.Int (Int8, Int16, Int32, Int64)
6438import Data.List (foldl')
6539import Data.Map (Map)
66−import Data.Monoid (Dual(..), First(..), Last(..))
6740import Data.Monoid (Monoid(..))
68−import Data.Ratio (Ratio)
6941import Data.String (IsString(..))
70−import Data.Text.Encoding (encodeUtf8)
71−import Data.Text (Text, pack, unpack)
72−import Data.Time.Clock (UTCTime)
73−import Data.Time.Format (FormatTime, formatTime, parseTime)
42+import Data.Text (Text, pack)
7443import Data.Typeable (Typeable)
7544import Data.Vector (Vector)
76−import Data.Word (Word, Word8, Word16, Word32, Word64)
77−import Foreign.Storable (Storable)
78−import System.Locale (defaultTimeLocale)
79−import qualified Data.ByteString as B
80−import qualified Data.ByteString.Lazy as LB
81−import qualified Data.HashMap.Strict as H
82−import qualified Data.HashSet as HashSet
83−import qualified Data.IntSet as IntSet
84−import qualified Data.IntMap as IntMap
8545import qualified Data.Map as M
86−import qualified Data.Set as Set
87−import qualified Data.Text as T
88−import qualified Data.Text.Lazy as LT
8946import qualified Data.Vector as V
90−import qualified Data.Vector.Storable as VS
91−import qualified Data.Vector.Primitive as VP
92−import qualified Data.Vector.Unboxed as VU
93−import qualified Data.Vector.Generic as VG
94−
95−#ifdef GENERICS
96−import GHC.Generics
97−#endif
9847
9948-- | The result of running a 'Parser'.
10049data Result a = Error String
@@ -259,19 +208,6 @@ isEmptyArray _ = False
259208emptyObject :: Value
260209emptyObject = Object M.empty
261210
262−-- | A key\/value pair for an 'Object'.
263−type Pair = (Text, Value)
264−
265−-- | Construct a 'Pair' from a key and a value.
266−(.=) :: ToJSON a => Text -> a -> Pair
267−name .= value = (name, toJSON value)
268−{-# INLINE (.=) #-}
269−
270−-- | Convert a value from JSON, failing if the types do not match.
271−fromJSON :: (FromJSON a) => Value -> Result a
272−fromJSON = parse parseJSON
273−{-# INLINE fromJSON #-}
274−
275211-- | Run a 'Parser'.
276212parse :: (a -> Parser b) -> a -> Result b
277213parse m v = runParser (m v) Error Success
@@ -285,900 +221,13 @@ parseMaybe m v = runParser (m v) (const Nothing) Just
285221-- | Run a 'Parser' with an 'Either' result type.
286222parseEither :: (a -> Parser b) -> a -> Either String b
287223parseEither m v = runParser (m v) Left Right
288−{-# INLINE parseEither #-}
289−
290−-- | Retrieve the value associated with the given key of an 'Object'.
291−-- The result is 'empty' if the key is not present or the value cannot
292−-- be converted to the desired type.
293−--
294−-- This accessor is appropriate if the key and value /must/ be present
295−-- in an object for it to be valid. If the key and value are
296−-- optional, use '(.:?)' instead.
297−(.:) :: (FromJSON a) => Object -> Text -> Parser a
298−obj .: key = case M.lookup key obj of
299− Nothing -> fail $ "key " ++ show key ++ " not present"
300− Just v -> parseJSON v
301−{-# INLINE (.:) #-}
302224
303−-- | Retrieve the value associated with the given key of an 'Object'.
304−-- The result is 'Nothing' if the key is not present, or 'empty' if
305−-- the value cannot be converted to the desired type.
306−--
307−-- This accessor is most useful if the key and value can be absent
308−-- from an object without affecting its validity. If the key and
309−-- value are mandatory, use '(.:)' instead.
310−(.:?) :: (FromJSON a) => Object -> Text -> Parser (Maybe a)
311−obj .:? key = case M.lookup key obj of
312− Nothing -> pure Nothing
313− Just v -> parseJSON v
314−{-# INLINE (.:?) #-}
225+-- | A key\/value pair for an 'Object'.
226+type Pair = (Text, Value)
315227
228+{-# INLINE parseEither #-}
316229-- | Create a 'Value' from a list of name\/value 'Pair's. If duplicate
317230-- keys arise, earlier keys and their associated values win.
318231object :: [Pair] -> Value
319232object = Object . M.fromList
320233{-# INLINE object #-}
321−
322−-- | A type that can be converted to JSON.
323−--
324−-- An example type and instance:
325−--
326−-- @{-\# LANGUAGE OverloadedStrings #-}
327−--
328−-- data Coord { x :: Double, y :: Double }
329−--
330−-- instance ToJSON Coord where
331−-- toJSON (Coord x y) = 'object' [\"x\" '.=' x, \"y\" '.=' y]
332−-- @
333−--
334−-- Note the use of the @OverloadedStrings@ language extension which enables
335−-- 'Text' values to be written as string literals.
336−--
337−-- Instead of manually writing your 'ToJSON' instance, there are three options
338−-- to do it automatically:
339−--
340−-- * 'Data.Aeson.TH' provides template-haskell functions which will derive an
341−-- instance at compile-time. The generated instance is optimized for your type
342−-- so will probably be more efficient than the following two options:
343−--
344−-- * 'Data.Aeson.Generic' provides a generic @toJSON@ function that accepts any
345−-- type which is an instance of 'Data'.
346−--
347−-- * If your compiler has support for the @DeriveGeneric@ and
348−-- @DefaultSignatures@ language extensions, @toJSON@ will have a default generic
349−-- implementation.
350−--
351−-- To use the latter option, simply add a @deriving 'Generic'@ clause to your
352−-- datatype and declare a @ToJSON@ instance for your datatype without giving a
353−-- definition for @toJSON@.
354−--
355−-- For example the previous example can be simplified to just:
356−--
357−-- @{-\# LANGUAGE DeriveGeneric \#-}
358−--
359−-- import GHC.Generics
360−--
361−-- data Coord { x :: Double, y :: Double } deriving Generic
362−--
363−-- instance ToJSON Coord
364−-- @
365−class ToJSON a where
366− toJSON :: a -> Value
367−
368−#ifdef GENERICS
369− default toJSON :: (Generic a, GToJSON (Rep a)) => a -> Value
370− toJSON = gToJSON . from
371−#endif
372−
373−-- | A type that can be converted from JSON, with the possibility of
374−-- failure.
375−--
376−-- When writing an instance, use 'mzero' or 'fail' to make a
377−-- conversion fail, e.g. if an 'Object' is missing a required key, or
378−-- the value is of the wrong type.
379−--
380−-- An example type and instance:
381−--
382−-- @{-\# LANGUAGE OverloadedStrings #-}
383−--
384−-- data Coord { x :: Double, y :: Double }
385−--
386−-- instance FromJSON Coord where
387−-- parseJSON ('Object' v) = Coord '<$>'
388−-- v '.:' \"x\" '<*>'
389−-- v '.:' \"y\"
390−--
391−-- \-- A non-'Object' value is of the wrong type, so use 'mzero' to fail.
392−-- parseJSON _ = 'mzero'
393−-- @
394−--
395−-- Note the use of the @OverloadedStrings@ language extension which enables
396−-- 'Text' values to be written as string literals.
397−--
398−-- Instead of manually writing your 'FromJSON' instance, there are three options
399−-- to do it automatically:
400−--
401−-- * 'Data.Aeson.TH' provides template-haskell functions which will derive an
402−-- instance at compile-time. The generated instance is optimized for your type
403−-- so will probably be more efficient than the following two options:
404−--
405−-- * 'Data.Aeson.Generic' provides a generic @fromJSON@ function that parses to
406−-- any type which is an instance of 'Data'.
407−--
408−-- * If your compiler has support for the @DeriveGeneric@ and
409−-- @DefaultSignatures@ language extensions, @parseJSON@ will have a default
410−-- generic implementation.
411−--
412−-- To use this, simply add a @deriving 'Generic'@ clause to your datatype and
413−-- declare a @FromJSON@ instance for your datatype without giving a definition
414−-- for @parseJSON@.
415−--
416−-- For example the previous example can be simplified to just:
417−--
418−-- @{-\# LANGUAGE DeriveGeneric \#-}
419−--
420−-- import GHC.Generics
421−--
422−-- data Coord { x :: Double, y :: Double } deriving Generic
423−--
424−-- instance FromJSON Coord
425−-- @
426−class FromJSON a where
427− parseJSON :: Value -> Parser a
428−
429−#ifdef GENERICS
430− default parseJSON :: (Generic a, GFromJSON (Rep a)) => Value -> Parser a
431− parseJSON = fmap to . gParseJSON
432−#endif
433−
434−instance (ToJSON a) => ToJSON (Maybe a) where
435− toJSON (Just a) = toJSON a
436− toJSON Nothing = Null
437− {-# INLINE toJSON #-}
438−
439−instance (FromJSON a) => FromJSON (Maybe a) where
440− parseJSON Null = pure Nothing
441− parseJSON a = Just <$> parseJSON a
442− {-# INLINE parseJSON #-}
443−
444−instance (ToJSON a, ToJSON b) => ToJSON (Either a b) where
445− toJSON (Left a) = object [left .= a]
446− toJSON (Right b) = object [right .= b]
447− {-# INLINE toJSON #-}
448−
449−instance (FromJSON a, FromJSON b) => FromJSON (Either a b) where
450− parseJSON (Object (M.toList -> [(key, value)]))
451− | key == left = Left <$> parseJSON value
452− | key == right = Right <$> parseJSON value
453− parseJSON _ = mzero
454− {-# INLINE parseJSON #-}
455−
456−left, right :: Text
457−left = "Left"
458−right = "Right"
459−
460−instance ToJSON Bool where
461− toJSON = Bool
462− {-# INLINE toJSON #-}
463−
464−instance FromJSON Bool where
465− parseJSON (Bool b) = pure b
466− parseJSON v = typeMismatch "Bool" v
467− {-# INLINE parseJSON #-}
468−
469−instance ToJSON () where
470− toJSON _ = emptyArray
471− {-# INLINE toJSON #-}
472−
473−instance FromJSON () where
474− parseJSON (Array v) | V.null v = pure ()
475− parseJSON v = typeMismatch "()" v
476− {-# INLINE parseJSON #-}
477−
478−instance ToJSON [Char] where
479− toJSON = String . T.pack
480− {-# INLINE toJSON #-}
481−
482−instance FromJSON [Char] where
483− parseJSON (String t) = pure (T.unpack t)
484− parseJSON v = typeMismatch "String" v
485− {-# INLINE parseJSON #-}
486−
487−instance ToJSON Char where
488− toJSON = String . T.singleton
489− {-# INLINE toJSON #-}
490−
491−instance FromJSON Char where
492− parseJSON (String t)
493− | T.compareLength t 1 == EQ = pure (T.head t)
494− parseJSON v = typeMismatch "Char" v
495− {-# INLINE parseJSON #-}
496−
497−instance ToJSON Double where
498− toJSON = Number . D
499− {-# INLINE toJSON #-}
500−
501−instance FromJSON Double where
502− parseJSON (Number n) = case n of
503− D d -> pure d
504− I i -> pure (fromIntegral i)
505− parseJSON Null = pure (0/0)
506− parseJSON v = typeMismatch "Double" v
507− {-# INLINE parseJSON #-}
508−
509−instance ToJSON Number where
510− toJSON = Number
511− {-# INLINE toJSON #-}
512−
513−instance FromJSON Number where
514− parseJSON (Number n) = pure n
515− parseJSON Null = pure (D (0/0))
516− parseJSON v = typeMismatch "Number" v
517− {-# INLINE parseJSON #-}
518−
519−instance ToJSON Float where
520− toJSON = Number . realToFrac
521− {-# INLINE toJSON #-}
522−
523−instance FromJSON Float where
524− parseJSON (Number n) = pure $ case n of
525− D d -> realToFrac d
526− I i -> fromIntegral i
527− parseJSON Null = pure (0/0)
528− parseJSON v = typeMismatch "Float" v
529− {-# INLINE parseJSON #-}
530−
531−instance ToJSON (Ratio Integer) where
532− toJSON = Number . fromRational
533− {-# INLINE toJSON #-}
534−
535−instance FromJSON (Ratio Integer) where
536− parseJSON (Number n) = pure $ case n of
537− D d -> toRational d
538− I i -> fromIntegral i
539− parseJSON v = typeMismatch "Ratio Integer" v
540− {-# INLINE parseJSON #-}
541−
542−instance ToJSON Int where
543− toJSON = Number . fromIntegral
544− {-# INLINE toJSON #-}
545−
546−instance FromJSON Int where
547− parseJSON = parseIntegral
548− {-# INLINE parseJSON #-}
549−
550−parseIntegral :: Integral a => Value -> Parser a
551−parseIntegral (Number n) = pure (floor n)
552−parseIntegral v = typeMismatch "Integral" v
553−{-# INLINE parseIntegral #-}
554−
555−instance ToJSON Integer where
556− toJSON = Number . fromIntegral
557− {-# INLINE toJSON #-}
558−
559−instance FromJSON Integer where
560− parseJSON = parseIntegral
561− {-# INLINE parseJSON #-}
562−
563−instance ToJSON Int8 where
564− toJSON = Number . fromIntegral
565− {-# INLINE toJSON #-}
566−
567−instance FromJSON Int8 where
568− parseJSON = parseIntegral
569− {-# INLINE parseJSON #-}
570−
571−instance ToJSON Int16 where
572− toJSON = Number . fromIntegral
573− {-# INLINE toJSON #-}
574−
575−instance FromJSON Int16 where
576− parseJSON = parseIntegral
577− {-# INLINE parseJSON #-}
578−
579−instance ToJSON Int32 where
580− toJSON = Number . fromIntegral
581− {-# INLINE toJSON #-}
582−
583−instance FromJSON Int32 where
584− parseJSON = parseIntegral
585− {-# INLINE parseJSON #-}
586−
587−instance ToJSON Int64 where
588− toJSON = Number . fromIntegral
589− {-# INLINE toJSON #-}
590−
591−instance FromJSON Int64 where
592− parseJSON = parseIntegral
593− {-# INLINE parseJSON #-}
594−
595−instance ToJSON Word where
596− toJSON = Number . fromIntegral
597− {-# INLINE toJSON #-}
598−
599−instance FromJSON Word where
600− parseJSON = parseIntegral
601− {-# INLINE parseJSON #-}
602−
603−instance ToJSON Word8 where
604− toJSON = Number . fromIntegral
605− {-# INLINE toJSON #-}
606−
607−instance FromJSON Word8 where
608− parseJSON = parseIntegral
609− {-# INLINE parseJSON #-}
610−
611−instance ToJSON Word16 where
612− toJSON = Number . fromIntegral
613− {-# INLINE toJSON #-}
614−
615−instance FromJSON Word16 where
616− parseJSON = parseIntegral
617− {-# INLINE parseJSON #-}
618−
619−instance ToJSON Word32 where
620− toJSON = Number . fromIntegral
621− {-# INLINE toJSON #-}
622−
623−instance FromJSON Word32 where
624− parseJSON = parseIntegral
625− {-# INLINE parseJSON #-}
626−
627−instance ToJSON Word64 where
628− toJSON = Number . fromIntegral
629− {-# INLINE toJSON #-}
630−
631−instance FromJSON Word64 where
632− parseJSON = parseIntegral
633− {-# INLINE parseJSON #-}
634−
635−instance ToJSON Text where
636− toJSON = String
637− {-# INLINE toJSON #-}
638−
639−instance FromJSON Text where
640− parseJSON (String t) = pure t
641− parseJSON v = typeMismatch "Text" v
642− {-# INLINE parseJSON #-}
643−
644−instance ToJSON LT.Text where
645− toJSON = String . LT.toStrict
646− {-# INLINE toJSON #-}
647−
648−instance FromJSON LT.Text where
649− parseJSON (String t) = pure (LT.fromStrict t)
650− parseJSON v = typeMismatch "Lazy Text" v
651− {-# INLINE parseJSON #-}
652−
653−instance ToJSON B.ByteString where
654− toJSON = String . decode
655− {-# INLINE toJSON #-}
656−
657−instance FromJSON B.ByteString where
658− parseJSON (String t) = pure . encodeUtf8 $ t
659− parseJSON v = typeMismatch "ByteString" v
660− {-# INLINE parseJSON #-}
661−
662−instance ToJSON LB.ByteString where
663− toJSON = toJSON . strict
664− {-# INLINE toJSON #-}
665−
666−instance FromJSON LB.ByteString where
667− parseJSON (String t) = pure . lazy $ t
668− parseJSON v = typeMismatch "Lazy ByteString" v
669− {-# INLINE parseJSON #-}
670−
671−instance (ToJSON a) => ToJSON [a] where
672− toJSON = Array . V.fromList . map toJSON
673− {-# INLINE toJSON #-}
674−
675−instance (FromJSON a) => FromJSON [a] where
676− parseJSON (Array a) = mapM parseJSON (V.toList a)
677− parseJSON v = typeMismatch "[a]" v
678− {-# INLINE parseJSON #-}
679−
680−instance (ToJSON a) => ToJSON (Vector a) where
681− toJSON = Array . V.map toJSON
682− {-# INLINE toJSON #-}
683−
684−instance (FromJSON a) => FromJSON (Vector a) where
685− parseJSON (Array a) = V.mapM parseJSON a
686− parseJSON v = typeMismatch "Vector a" v
687− {-# INLINE parseJSON #-}
688−
689−vectorToJSON :: (VG.Vector v a, ToJSON a) => v a -> Value
690−vectorToJSON = Array . V.map toJSON . V.convert
691−{-# INLINE vectorToJSON #-}
692−
693−vectorParseJSON :: (FromJSON a, VG.Vector w a) => String -> Value -> Parser (w a)
694−vectorParseJSON _ (Array a) = V.convert <$> V.mapM parseJSON a
695−vectorParseJSON s v = typeMismatch s v
696−{-# INLINE vectorParseJSON #-}
697−
698−instance (Storable a, ToJSON a) => ToJSON (VS.Vector a) where
699− toJSON = vectorToJSON
700−
701−instance (Storable a, FromJSON a) => FromJSON (VS.Vector a) where
702− parseJSON = vectorParseJSON "Data.Vector.Storable.Vector a"
703−
704−instance (VP.Prim a, ToJSON a) => ToJSON (VP.Vector a) where
705− toJSON = vectorToJSON
706−
707−instance (VP.Prim a, FromJSON a) => FromJSON (VP.Vector a) where
708− parseJSON = vectorParseJSON "Data.Vector.Primitive.Vector a"
709−
710−instance (VG.Vector VU.Vector a, ToJSON a) => ToJSON (VU.Vector a) where
711− toJSON = vectorToJSON
712−
713−instance (VG.Vector VU.Vector a, FromJSON a) => FromJSON (VU.Vector a) where
714− parseJSON = vectorParseJSON "Data.Vector.Unboxed.Vector a"
715−
716−instance (ToJSON a) => ToJSON (Set.Set a) where
717− toJSON = toJSON . Set.toList
718− {-# INLINE toJSON #-}
719−
720−instance (Ord a, FromJSON a) => FromJSON (Set.Set a) where
721− parseJSON = fmap Set.fromList . parseJSON
722− {-# INLINE parseJSON #-}
723−
724−instance (ToJSON a) => ToJSON (HashSet.HashSet a) where
725− toJSON = toJSON . HashSet.toList
726− {-# INLINE toJSON #-}
727−
728−instance (Eq a, Hashable a, FromJSON a) => FromJSON (HashSet.HashSet a) where
729− parseJSON = fmap HashSet.fromList . parseJSON
730− {-# INLINE parseJSON #-}
731−
732−instance ToJSON IntSet.IntSet where
733− toJSON = toJSON . IntSet.toList
734− {-# INLINE toJSON #-}
735−
736−instance FromJSON IntSet.IntSet where
737− parseJSON = fmap IntSet.fromList . parseJSON
738− {-# INLINE parseJSON #-}
739−
740−instance ToJSON a => ToJSON (IntMap.IntMap a) where
741− toJSON = toJSON . IntMap.toList
742− {-# INLINE toJSON #-}
743−
744−instance FromJSON a => FromJSON (IntMap.IntMap a) where
745− parseJSON = fmap IntMap.fromList . parseJSON
746− {-# INLINE parseJSON #-}
747−
748−instance (ToJSON v) => ToJSON (M.Map Text v) where
749− toJSON = Object . M.map toJSON
750− {-# INLINE toJSON #-}
751−
752−instance (FromJSON v) => FromJSON (M.Map Text v) where
753− parseJSON (Object o) = M.fromAscList <$> mapM go (M.toAscList o)
754− where go (k,v) = ((,) k) <$> parseJSON v
755− parseJSON v = typeMismatch "Map Text a" v
756−
757−instance (ToJSON v) => ToJSON (M.Map LT.Text v) where
758− toJSON = Object . transformMap LT.toStrict toJSON
759−
760−instance (FromJSON v) => FromJSON (M.Map LT.Text v) where
761− parseJSON = fmap (M.mapKeysMonotonic LT.fromStrict) . parseJSON
762−
763−instance (ToJSON v) => ToJSON (M.Map String v) where
764− toJSON = Object . transformMap pack toJSON
765−
766−instance (FromJSON v) => FromJSON (M.Map String v) where
767− parseJSON = fmap (M.mapKeysMonotonic unpack) . parseJSON
768−
769−instance (ToJSON v) => ToJSON (M.Map B.ByteString v) where
770− toJSON = Object . transformMap decode toJSON
771−
772−instance (FromJSON v) => FromJSON (M.Map B.ByteString v) where
773− parseJSON = fmap (M.mapKeysMonotonic encodeUtf8) . parseJSON
774−
775−instance (ToJSON v) => ToJSON (M.Map LB.ByteString v) where
776− toJSON = Object . transformMap strict toJSON
777−
778−instance (FromJSON v) => FromJSON (M.Map LB.ByteString v) where
779− parseJSON = fmap (M.mapKeysMonotonic lazy) . parseJSON
780−
781−instance (ToJSON v) => ToJSON (H.HashMap Text v) where
782− toJSON = Object . hashMap id toJSON
783− {-# INLINE toJSON #-}
784−
785−instance (FromJSON v) => FromJSON (H.HashMap Text v) where
786− parseJSON (Object o) = H.fromList <$> mapM go (M.toList o)
787− where go (k,v) = ((,) k) <$> parseJSON v
788− parseJSON v = typeMismatch "HashMap Text a" v
789−
790−instance (ToJSON v) => ToJSON (H.HashMap LT.Text v) where
791− toJSON = Object . M.fromList . H.foldrWithKey (\k v -> ((LT.toStrict k,toJSON v) :)) []
792−
793−instance (FromJSON v) => FromJSON (H.HashMap LT.Text v) where
794− parseJSON = fmap (mapHash LT.fromStrict) . parseJSON
795−
796−instance (ToJSON v) => ToJSON (H.HashMap String v) where
797− toJSON = Object . hashMap pack toJSON
798−
799−instance (FromJSON v) => FromJSON (H.HashMap String v) where
800− parseJSON = fmap (mapHash unpack) . parseJSON
801−
802−instance (ToJSON v) => ToJSON (H.HashMap B.ByteString v) where
803− toJSON = Object . hashMap decode toJSON
804−
805−instance (FromJSON v) => FromJSON (H.HashMap B.ByteString v) where
806− parseJSON = fmap (mapHash encodeUtf8) . parseJSON
807−
808−instance (ToJSON v) => ToJSON (H.HashMap LB.ByteString v) where
809− toJSON = Object . hashMap strict toJSON
810−
811−instance (FromJSON v) => FromJSON (H.HashMap LB.ByteString v) where
812− parseJSON = fmap (mapHash lazy) . parseJSON
813−
814−instance ToJSON Value where
815− toJSON a = a
816− {-# INLINE toJSON #-}
817−
818−instance FromJSON Value where
819− parseJSON a = pure a
820− {-# INLINE parseJSON #-}
821−
822−-- | A newtype wrapper for 'UTCTime' that uses the same non-standard
823−-- serialization format as Microsoft .NET, whose @System.DateTime@
824−-- type is by default serialized to JSON as in the following example:
825−--
826−-- > /Date(1302547608878)/
827−--
828−-- The number represents milliseconds since the Unix epoch.
829−newtype DotNetTime = DotNetTime {
830− fromDotNetTime :: UTCTime
831− } deriving (Eq, Ord, Read, Show, Typeable, FormatTime)
832−
833−instance ToJSON DotNetTime where
834− toJSON (DotNetTime t) =
835− String (pack (secs ++ msecs ++ ")/"))
836− where secs = formatTime defaultTimeLocale "/Date(%s" t
837− msecs = take 3 $ formatTime defaultTimeLocale "%q" t
838− {-# INLINE toJSON #-}
839−
840−instance FromJSON DotNetTime where
841− parseJSON (String t) =
842− case parseTime defaultTimeLocale "/Date(%s%Q)/" (unpack t') of
843− Just d -> pure (DotNetTime d)
844− _ -> fail "could not parse .NET time"
845− where (s,m) = T.splitAt (T.length t - 5) t
846− t' = T.concat [s,".",m]
847− parseJSON v = typeMismatch "DotNetTime" v
848− {-# INLINE parseJSON #-}
849−
850−instance ToJSON UTCTime where
851− toJSON t = String (pack (take 23 str ++ "Z"))
852− where str = formatTime defaultTimeLocale "%FT%T%Q" t
853− {-# INLINE toJSON #-}
854−
855−instance FromJSON UTCTime where
856− parseJSON (String t) =
857− case parseTime defaultTimeLocale "%FT%T%QZ" (unpack t) of
858− Just d -> pure d
859− _ -> fail "could not parse ISO-8601 date"
860− parseJSON v = typeMismatch "UTCTime" v
861− {-# INLINE parseJSON #-}
862−
863−instance (ToJSON a, ToJSON b) => ToJSON (a,b) where
864− toJSON (a,b) = toJSON [toJSON a, toJSON b]
865− {-# INLINE toJSON #-}
866−
867−instance (FromJSON a, FromJSON b) => FromJSON (a,b) where
868− parseJSON (Array ab) =
869− case V.toList ab of
870− [a,b] -> (,) <$> parseJSON a <*> parseJSON b
871− _ -> fail $ "cannot unpack array of length " ++
872− show (V.length ab) ++ " into a pair"
873− parseJSON v = typeMismatch "(a,b)" v
874− {-# INLINE parseJSON #-}
875−
876−instance (ToJSON a, ToJSON b, ToJSON c) => ToJSON (a,b,c) where
877− toJSON (a,b,c) = toJSON [toJSON a, toJSON b, toJSON c]
878− {-# INLINE toJSON #-}
879−
880−instance (FromJSON a, FromJSON b, FromJSON c) => FromJSON (a,b,c) where
881− parseJSON (Array abc) =
882− case V.toList abc of
883− [a,b,c] -> (,,) <$> parseJSON a <*> parseJSON b <*> parseJSON c
884− _ -> fail $ "cannot unpack array of length " ++
885− show (V.length abc) ++ " into a 3-tuple"
886− parseJSON v = typeMismatch "(a,b,c)" v
887− {-# INLINE parseJSON #-}
888−
889−instance ToJSON a => ToJSON (Dual a) where
890− toJSON = toJSON . getDual
891− {-# INLINE toJSON #-}
892−
893−instance FromJSON a => FromJSON (Dual a) where
894− parseJSON = fmap Dual . parseJSON
895− {-# INLINE parseJSON #-}
896−
897−instance ToJSON a => ToJSON (First a) where
898− toJSON = toJSON . getFirst
899− {-# INLINE toJSON #-}
900−
901−instance FromJSON a => FromJSON (First a) where
902− parseJSON = fmap First . parseJSON
903− {-# INLINE parseJSON #-}
904−
905−instance ToJSON a => ToJSON (Last a) where
906− toJSON = toJSON . getLast
907− {-# INLINE toJSON #-}
908−
909−instance FromJSON a => FromJSON (Last a) where
910− parseJSON = fmap Last . parseJSON
911− {-# INLINE parseJSON #-}
912−
913−-- | Fail parsing due to a type mismatch, with a descriptive message.
914−typeMismatch :: String -- ^ The name of the type you are trying to parse.
915− -> Value -- ^ The actual value encountered.
916− -> Parser a
917−typeMismatch expected actual =
918− fail $ "when expecting a " ++ expected ++ ", encountered " ++ name ++
919− " instead"
920− where
921− name = case actual of
922− Object _ -> "Object"
923− Array _ -> "Array"
924− String _ -> "String"
925− Number _ -> "Number"
926− Bool _ -> "Boolean"
927− Null -> "Null"
928−
929−#ifdef GENERICS
930−--------------------------------------------------------------------------------
931−-- Generic toJSON
932−
933−class GToJSON f where
934− gToJSON :: f a -> Value
935−
936−instance (GToJSON a) => GToJSON (M1 i c a) where
937− gToJSON = gToJSON . unM1
938− {-# INLINE gToJSON #-}
939−
940−instance (ToJSON a) => GToJSON (K1 i a) where
941− gToJSON = toJSON . unK1
942− {-# INLINE gToJSON #-}
943−
944−instance GToJSON U1 where
945− gToJSON _ = emptyArray
946− {-# INLINE gToJSON #-}
947−
948−instance (ConsToJSON a) => GToJSON (C1 c a) where
949− gToJSON = consToJSON . unM1
950− {-# INLINE gToJSON #-}
951−
952−instance (GProductToValues a, GProductToValues b) => GToJSON (a :*: b) where
953− gToJSON = toJSON . toList . gProductToValues
954− {-# INLINE gToJSON #-}
955−
956−instance (GObject a, GObject b) => GToJSON (a :+: b) where
957− gToJSON (L1 x) = Object $ gObject x
958− gToJSON (R1 x) = Object $ gObject x
959− {-# INLINE gToJSON #-}
960−
961−--------------------------------------------------------------------------------
962−
963−class ConsToJSON f where consToJSON :: f a -> Value
964−class ConsToJSON' b f where consToJSON' :: Tagged b (f a -> Value)
965−
966−newtype Tagged s b = Tagged {unTagged :: b}
967−
968−instance (IsRecord f b, ConsToJSON' b f) => ConsToJSON f where
969− consToJSON = unTagged (consToJSON' :: Tagged b (f a -> Value))
970− {-# INLINE consToJSON #-}
971−
972−instance (GRecordToPairs f) => ConsToJSON' True f where
973− consToJSON' = Tagged (object . toList . gRecordToPairs)
974− {-# INLINE consToJSON' #-}
975−
976−instance GToJSON f => ConsToJSON' False f where
977− consToJSON' = Tagged gToJSON
978− {-# INLINE consToJSON' #-}
979−
980−--------------------------------------------------------------------------------
981−
982−class GRecordToPairs f where
983− gRecordToPairs :: f a -> DList Pair
984−
985−instance (GRecordToPairs a, GRecordToPairs b) => GRecordToPairs (a :*: b) where
986− gRecordToPairs (a :*: b) = gRecordToPairs a `append` gRecordToPairs b
987− {-# INLINE gRecordToPairs #-}
988−
989−instance (Selector s, GToJSON a) => GRecordToPairs (S1 s a) where
990− gRecordToPairs m1 = singleton (pack (selName m1), gToJSON (unM1 m1))
991− {-# INLINE gRecordToPairs #-}
992−
993−--------------------------------------------------------------------------------
994−
995−class GProductToValues f where
996− gProductToValues :: f a -> DList Value
997−
998−instance (GProductToValues a, GProductToValues b) => GProductToValues (a :*: b) where
999− gProductToValues (a :*: b) = gProductToValues a `append` gProductToValues b
1000− {-# INLINE gProductToValues #-}
1001−
1002−instance (GToJSON a) => GProductToValues a where
1003− gProductToValues = singleton . gToJSON
1004− {-# INLINE gProductToValues #-}
1005−
1006−--------------------------------------------------------------------------------
1007−
1008−class GObject f where
1009− gObject :: f a -> Object
1010−
1011−instance (GObject a, GObject b) => GObject (a :+: b) where
1012− gObject (L1 x) = gObject x
1013− gObject (R1 x) = gObject x
1014− {-# INLINE gObject #-}
1015−
1016−instance (Constructor c, GToJSON a, ConsToJSON a) => GObject (C1 c a) where
1017− gObject = M.singleton (pack $ conName (undefined :: t c a p)) . gToJSON
1018− {-# INLINE gObject #-}
1019−
1020−--------------------------------------------------------------------------------
1021−-- Generic parseJSON
1022−
1023−class GFromJSON f where
1024− gParseJSON :: Value -> Parser (f a)
1025−
1026−instance (GFromJSON a) => GFromJSON (M1 i c a) where
1027− gParseJSON = fmap M1 . gParseJSON
1028− {-# INLINE gParseJSON #-}
1029−
1030−instance (FromJSON a) => GFromJSON (K1 i a) where
1031− gParseJSON = fmap K1 . parseJSON
1032− {-# INLINE gParseJSON #-}
1033−
1034−instance GFromJSON U1 where
1035− gParseJSON v
1036− | isEmptyArray v = pure U1
1037− | otherwise = typeMismatch "unit constructor (U1)" v
1038− {-# INLINE gParseJSON #-}
1039−
1040−instance (ConsFromJSON a) => GFromJSON (C1 c a) where
1041− gParseJSON = fmap M1 . consParseJSON
1042− {-# INLINE gParseJSON #-}
1043−
1044−instance ( GFromProduct a, GFromProduct b
1045− , ProductSize a, ProductSize b) => GFromJSON (a :*: b) where
1046− gParseJSON (Array arr)
1047− | lenArray == lenProduct = gParseProduct arr 0 lenProduct
1048− | otherwise =
1049− fail $ "When expecting a product of " ++ show lenProduct ++
1050− " values, encountered an Array of " ++ show lenArray ++
1051− " elements instead"
1052− where
1053− lenArray = V.length arr
1054− lenProduct = unTagged2 (productSize :: Tagged2 (a :*: b) Int)
1055−
1056− gParseJSON v = typeMismatch "product (:*:)" v
1057− {-# INLINE gParseJSON #-}
1058−
1059−instance (GFromSum a, GFromSum b) => GFromJSON (a :+: b) where
1060− gParseJSON (Object (M.toList -> [keyVal@(key, _)])) =
1061− case gParseSum keyVal of
1062− Nothing -> notFound $ unpack key
1063− Just p -> p
1064− gParseJSON v = typeMismatch "sum (:+:)" v
1065− {-# INLINE gParseJSON #-}
1066−
1067−notFound :: String -> Parser a
1068−notFound key = fail $ "The key \"" ++ key ++ "\" was not found"
1069−{-# INLINE notFound #-}
1070−
1071−--------------------------------------------------------------------------------
1072−
1073−class ConsFromJSON f where consParseJSON :: Value -> Parser (f a)
1074−class ConsFromJSON' b f where consParseJSON' :: Tagged b (Value -> Parser (f a))
1075−
1076−instance (IsRecord f b, ConsFromJSON' b f) => ConsFromJSON f where
1077− consParseJSON = unTagged (consParseJSON' :: Tagged b (Value -> Parser (f a)))
1078− {-# INLINE consParseJSON #-}
1079−
1080−instance (GFromRecord f) => ConsFromJSON' True f where
1081− consParseJSON' = Tagged parseRecord
1082− where
1083− parseRecord (Object obj) = gParseRecord obj
1084− parseRecord v = typeMismatch "record (:*:)" v
1085− {-# INLINE consParseJSON' #-}
1086−
1087−instance (GFromJSON f) => ConsFromJSON' False f where
1088− consParseJSON' = Tagged gParseJSON
1089− {-# INLINE consParseJSON' #-}
1090−
1091−--------------------------------------------------------------------------------
1092−
1093−class GFromRecord f where
1094− gParseRecord :: Object -> Parser (f a)
1095−
1096−instance (GFromRecord a, GFromRecord b) => GFromRecord (a :*: b) where
1097− gParseRecord obj = (:*:) <$> gParseRecord obj <*> gParseRecord obj
1098− {-# INLINE gParseRecord #-}
1099−
1100−instance (Selector s, GFromJSON a) => GFromRecord (S1 s a) where
1101− gParseRecord = maybe (notFound key) gParseJSON . M.lookup (T.pack key)
1102− where
1103− key = selName (undefined :: t s a p)
1104− {-# INLINE gParseRecord #-}
1105−
1106−--------------------------------------------------------------------------------
1107−
1108−class ProductSize f where
1109− productSize :: Tagged2 f Int
1110−
1111−newtype Tagged2 (s :: * -> *) b = Tagged2 {unTagged2 :: b}
1112−
1113−instance (ProductSize a, ProductSize b) => ProductSize (a :*: b) where
1114− productSize = Tagged2 $ unTagged2 (productSize :: Tagged2 a Int) +
1115− unTagged2 (productSize :: Tagged2 b Int)
1116−
1117−instance ProductSize (S1 s a) where
1118− productSize = Tagged2 1
1119−
1120−--------------------------------------------------------------------------------
1121−
1122−class GFromProduct f where
1123− gParseProduct :: Array -> Int -> Int -> Parser (f a)
1124−
1125−instance (GFromProduct a, GFromProduct b) => GFromProduct (a :*: b) where
1126− gParseProduct arr ix len = (:*:) <$> gParseProduct arr ix lenL
1127− <*> gParseProduct arr ixR lenR
1128− where
1129− lenL = len `shiftR` 1
1130− ixR = ix + lenL
1131− lenR = len - lenL
1132− {-# INLINE gParseProduct #-}
1133−
1134−instance (GFromJSON a) => GFromProduct (S1 s a) where
1135− gParseProduct arr ix _ = gParseJSON $ V.unsafeIndex arr ix
1136− {-# INLINE gParseProduct #-}
1137−
1138−--------------------------------------------------------------------------------
1139−
1140−class GFromSum f where
1141− gParseSum :: Pair -> Maybe (Parser (f a))
1142−
1143−instance (GFromSum a, GFromSum b) => GFromSum (a :+: b) where
1144− gParseSum keyVal = (fmap L1 <$> gParseSum keyVal) <|>
1145− (fmap R1 <$> gParseSum keyVal)
1146− {-# INLINE gParseSum #-}
1147−
1148−instance (Constructor c, GFromJSON a, ConsFromJSON a) => GFromSum (C1 c a) where
1149− gParseSum (key, value)
1150− | key == pack (conName (undefined :: t c a p)) = Just $ gParseJSON value
1151− | otherwise = Nothing
1152− {-# INLINE gParseSum #-}
1153−
1154−--------------------------------------------------------------------------------
1155−
1156−class IsRecord (f :: * -> *) b | f -> b
1157−
1158−data True
1159−data False
1160−
1161−instance (IsRecord f b) => IsRecord (f :*: g) b
1162−instance IsRecord (M1 S NoSelector f) False
1163−instance (IsRecord f b) => IsRecord (M1 S c f) b
1164−instance IsRecord (K1 i c) True
1165−instance IsRecord U1 False
1166−
1167−--------------------------------------------------------------------------------
1168−
1169−type DList a = [a] -> [a]
1170−
1171−toList :: DList a -> [a]
1172−toList = ($ [])
1173−{-# INLINE toList #-}
1174−
1175−singleton :: a -> DList a
1176−singleton = (:)
1177−{-# INLINE singleton #-}
1178−
1179−append :: DList a -> DList a -> DList a
1180−append = (.)
1181−{-# INLINE append #-}
1182−
1183−--------------------------------------------------------------------------------
1184−#endif
aeson.cabal+7−4
@@ -116,8 +116,15 @@ library
116116
117117 other-modules:
118118 Data.Aeson.Functions
119+ Data.Aeson.Types.Class
119120 Data.Aeson.Types.Internal
120121
122+ if impl(ghc >= 7.2.1)
123+ cpp-options: -DGENERICS
124+ build-depends: ghc-prim >= 0.2
125+ other-modules:
126+ Data.Aeson.Types.Generic
127+
121128 build-depends:
122129 attoparsec >= 0.8.6.1,
123130 base == 4.*,
@@ -140,10 +147,6 @@ library
140147 ghc-options: -Werror
141148 ghc-prof-options: -auto-all
142149
143− if impl(ghc >= 7.2.1)
144− cpp-options: -DGENERICS
145− build-depends: ghc-prim >= 0.2
146−
147150 ghc-options: -Wall
148151
149152test-suite tests