on master help sign in
Partially implement generic JSON support.

Borrowed from the json package's implementation.

commitd633b7adaafbe816cdb970db09d61567c5f5cf02 authorBryan O'Sullivan <bos@serpentine.com> dateThu 3 Feb 2011, 22:18 (16 years ago) parent5c6062f
4 files changed+274−16
Data/Aeson/Functions.hs+13 Data/Aeson/Generic.hs+109 Data/Aeson/Types.hs+147−16 aeson.cabal+5
Data/Aeson/Functions.hsnew file+13
@@ -0,0 +1,13 @@
1+module Data.Aeson.Functions
2+ (
3+ transformMap
4+ ) where
5+
6+import Control.Arrow ((***))
7+import qualified Data.Map as M
8+
9+-- | Transform one map into another. The ordering of keys must be
10+-- preserved.
11+transformMap :: (Ord k1, Ord k2) => (k1 -> k2) -> (v1 -> v2)
12+ -> M.Map k1 v1 -> M.Map k2 v2
13+transformMap fk fv = M.fromAscList . map (fk *** fv) . M.toAscList
Data/Aeson/Generic.hsnew file+109
@@ -0,0 +1,109 @@
1+module Data.Aeson.Generic
2+ (
3+ fromJSON
4+ , toJSON
5+ ) where
6+
7+import Control.Applicative (Alternative)
8+import Data.Aeson.Functions
9+import Data.Aeson.Types hiding (FromJSON(..), ToJSON(..))
10+import Data.Generics
11+import Data.Int
12+import Data.IntSet (IntSet)
13+import Data.Text (Text, pack)
14+import Data.Time.Clock (UTCTime)
15+import Data.Word
16+import qualified Data.Aeson.Types as T
17+import qualified Data.ByteString as B
18+import qualified Data.ByteString.Lazy as L
19+import qualified Data.Map as Map
20+import qualified Data.Set as Set
21+import qualified Data.Text.Lazy as L
22+import qualified Data.Vector as V
23+
24+type T a = a -> Value
25+
26+toJSON :: (Data a) => a -> Value
27+toJSON = toJSON_generic
28+ `ext1Q` list
29+ `ext1Q` vector
30+ `ext1Q` set
31+ `ext1Q` mapText
32+ `ext1Q` mapLazyText
33+ `ext1Q` mapString
34+ -- Use the standard encoding for all base types.
35+ `extQ` (T.toJSON :: T Integer)
36+ `extQ` (T.toJSON :: T Int)
37+ `extQ` (T.toJSON :: T Int8)
38+ `extQ` (T.toJSON :: T Int16)
39+ `extQ` (T.toJSON :: T Int32)
40+ `extQ` (T.toJSON :: T Int64)
41+ `extQ` (T.toJSON :: T Word)
42+ `extQ` (T.toJSON :: T Word8)
43+ `extQ` (T.toJSON :: T Word16)
44+ `extQ` (T.toJSON :: T Word32)
45+ `extQ` (T.toJSON :: T Word64)
46+ `extQ` (T.toJSON :: T Double)
47+ `extQ` (T.toJSON :: T Float)
48+ `extQ` (T.toJSON :: T Rational)
49+ `extQ` (T.toJSON :: T Char)
50+ `extQ` (T.toJSON :: T Text)
51+ `extQ` (T.toJSON :: T L.Text)
52+ `extQ` (T.toJSON :: T String)
53+ `extQ` (T.toJSON :: T B.ByteString)
54+ `extQ` (T.toJSON :: T L.ByteString)
55+ `extQ` (T.toJSON :: T T.Value)
56+ `extQ` (T.toJSON :: T UTCTime)
57+ `extQ` (T.toJSON :: T IntSet)
58+ `extQ` (T.toJSON :: T Bool)
59+ `extQ` (T.toJSON :: T ())
60+ --`extQ` (T.toJSON :: T Ordering)
61+ where
62+ list xs = Array . V.fromList . map toJSON $ xs
63+ vector v = Array . V.map toJSON $ v
64+ set s = Array . V.fromList . map toJSON . Set.toList $ s
65+ mapText m = Object . Map.map toJSON $ m
66+ mapLazyText m = Object . transformMap L.toStrict toJSON $ m
67+ mapString m = Object . transformMap pack toJSON $ m
68+
69+toJSON_generic :: (Data a) => a -> Value
70+toJSON_generic = generic
71+ where
72+ -- Generic encoding of an algebraic data type.
73+ generic a =
74+ case dataTypeRep (dataTypeOf a) of
75+ -- No constructor, so it must be an error value. Code
76+ -- it anyway as Null.
77+ AlgRep [] -> Null
78+ -- Elide a single constructor and just code the arguments.
79+ AlgRep [c] -> encodeArgs c (gmapQ toJSON a)
80+ -- For multiple constructors, make an object with a
81+ -- field name that is the constructor (except lower
82+ -- case) and the data is the arguments encoded.
83+ AlgRep _ -> encodeConstr (toConstr a) (gmapQ toJSON a)
84+ rep -> err (dataTypeOf a) rep
85+ where
86+ err dt r = error $ "Data.Aeson.Generic.toJSON: not AlgRep " ++
87+ show r ++ "(" ++ show dt ++ ")"
88+ -- Encode nullary constructor as a string.
89+ -- Encode non-nullary constructors as an object with the constructor
90+ -- name as the single field and the arguments as the value.
91+ -- Use an array if the are no field names, but elide singleton arrays,
92+ -- and use an object if there are field names.
93+ encodeConstr c [] = String . constrString $ c
94+ encodeConstr c as = object [(constrString c, encodeArgs c as)]
95+
96+ constrString = pack . showConstr
97+
98+ encodeArgs c = encodeArgs' (constrFields c)
99+ encodeArgs' [] [j] = j
100+ encodeArgs' [] js = Array . V.fromList $ js
101+ encodeArgs' ns js = object $ zip (map mungeField ns) js
102+
103+ -- Skip leading '_' in field name so we can use keywords
104+ -- etc. as field names.
105+ mungeField ('_':cs) = pack cs
106+ mungeField cs = pack cs
107+
108+fromJSON :: (Alternative f, Data a) => Value -> f a
109+fromJSON = undefined
Data/Aeson/Types.hs+147−16
@@ -1,4 +1,5 @@
1−{-# LANGUAGE DeriveDataTypeable, FlexibleInstances #-}
1+{-# LANGUAGE DeriveDataTypeable, FlexibleInstances, IncoherentInstances,
2+ OverlappingInstances #-}
23
34-- Module: Data.Aeson.Types
45-- Copyright: (c) 2011 MailRank, Inc.
@@ -28,25 +29,30 @@ module Data.Aeson.Types
2829 , object
2930 ) where
3031
31−import Control.Arrow ((***))
3232import Control.Applicative
3333import Control.DeepSeq (NFData(..))
3434import Data.Data (Data)
35+import Data.Int (Int8, Int16, Int32, Int64)
36+import qualified Data.IntSet as IntSet
3537import Data.Map (Map)
3638import Data.Monoid (Dual(..), First(..), Last(..))
39+import Data.Ratio (Ratio)
3740import Data.Text (Text, pack, unpack)
3841import Data.Text.Encoding (decodeUtf8, encodeUtf8)
3942import Data.Time.Clock (UTCTime)
4043import Data.Time.Format (formatTime, parseTime)
4144import Data.Typeable (Typeable)
4245import Data.Vector (Vector)
46+import Data.Word (Word, Word8, Word16, Word32, Word64)
4347import System.Locale (defaultTimeLocale)
4448import qualified Data.ByteString as B
4549import qualified Data.ByteString.Lazy as LB
4650import qualified Data.Map as M
4751import qualified Data.Set as Set
52+import qualified Data.Text as T
4853import qualified Data.Text.Lazy as LT
4954import qualified Data.Vector as V
55+import Data.Aeson.Functions
5056
5157-- | A JSON \"object\" (key\/value map).
5258type Object = Map Text Value
@@ -80,15 +86,11 @@ emptyObject :: Value
8086emptyObject = Object M.empty
8187
8288-- | A key\/value pair for an 'Object'.
83−newtype Pair = Pair { unPair :: (Text, Value) }
84− deriving (Eq, Typeable)
85−
86−instance Show Pair where
87− show = show . unPair
89+type Pair = (Text, Value)
8890
8991-- | Construct a 'Pair' from a key and a value.
9092(.=) :: ToJSON a => Text -> a -> Pair
91−name .= value = Pair (name, toJSON value)
93+name .= value = (name, toJSON value)
9294{-# INLINE (.=) #-}
9395
9496-- | Retrieve the value associated with the given key of an 'Object'.
@@ -117,10 +119,10 @@ obj .:? key = case M.lookup key obj of
117119 Just v -> fromJSON v
118120{-# INLINE (.:?) #-}
119121
120−-- | Create a 'Value' from a list of 'Pair's. If duplicate
122+-- | Create a 'Value' from a list of name\/value 'Pair's. If duplicate
121123-- keys arise, earlier keys and their associated values win.
122124object :: [Pair] -> Value
123−object = Object . M.fromList . map unPair
125+object = Object . M.fromList
124126{-# INLINE object #-}
125127
126128-- | A type that can be converted to JSON.
@@ -185,6 +187,34 @@ instance FromJSON Bool where
185187 fromJSON _ = empty
186188 {-# INLINE fromJSON #-}
187189
190+instance ToJSON () where
191+ toJSON _ = emptyArray
192+ {-# INLINE toJSON #-}
193+
194+instance FromJSON () where
195+ fromJSON (Array v) | V.null v = pure ()
196+ fromJSON _ = empty
197+ {-# INLINE fromJSON #-}
198+
199+instance ToJSON [Char] where
200+ toJSON = String . T.pack
201+ {-# INLINE toJSON #-}
202+
203+instance FromJSON [Char] where
204+ fromJSON (String t) = pure (T.unpack t)
205+ fromJSON _ = empty
206+ {-# INLINE fromJSON #-}
207+
208+instance ToJSON Char where
209+ toJSON = String . T.singleton
210+ {-# INLINE toJSON #-}
211+
212+instance FromJSON Char where
213+ fromJSON (String t)
214+ | T.compareLength t 1 == EQ = pure (T.head t)
215+ fromJSON _ = empty
216+ {-# INLINE fromJSON #-}
217+
188218instance ToJSON Double where
189219 toJSON = Number
190220 {-# INLINE toJSON #-}
@@ -194,6 +224,24 @@ instance FromJSON Double where
194224 fromJSON _ = empty
195225 {-# INLINE fromJSON #-}
196226
227+instance ToJSON Float where
228+ toJSON = Number . fromRational . toRational
229+ {-# INLINE toJSON #-}
230+
231+instance FromJSON Float where
232+ fromJSON (Number n) = pure . fromRational . toRational $ n
233+ fromJSON _ = empty
234+ {-# INLINE fromJSON #-}
235+
236+instance ToJSON (Ratio Integer) where
237+ toJSON = Number . fromRational
238+ {-# INLINE toJSON #-}
239+
240+instance FromJSON (Ratio Integer) where
241+ fromJSON (Number n) = pure . toRational $ n
242+ fromJSON _ = empty
243+ {-# INLINE fromJSON #-}
244+
197245instance ToJSON Int where
198246 toJSON = Number . fromIntegral
199247 {-# INLINE toJSON #-}
@@ -212,6 +260,87 @@ instance FromJSON Integer where
212260 fromJSON _ = empty
213261 {-# INLINE fromJSON #-}
214262
263+instance ToJSON Int8 where
264+ toJSON = Number . fromIntegral
265+ {-# INLINE toJSON #-}
266+
267+instance FromJSON Int8 where
268+ fromJSON (Number n) = pure (floor n)
269+ fromJSON _ = empty
270+ {-# INLINE fromJSON #-}
271+
272+instance ToJSON Int16 where
273+ toJSON = Number . fromIntegral
274+ {-# INLINE toJSON #-}
275+
276+instance FromJSON Int16 where
277+ fromJSON (Number n) = pure (floor n)
278+ fromJSON _ = empty
279+ {-# INLINE fromJSON #-}
280+
281+instance ToJSON Int32 where
282+ toJSON = Number . fromIntegral
283+ {-# INLINE toJSON #-}
284+
285+instance FromJSON Int32 where
286+ fromJSON (Number n) = pure (floor n)
287+ fromJSON _ = empty
288+ {-# INLINE fromJSON #-}
289+
290+instance ToJSON Int64 where
291+ toJSON = Number . fromIntegral
292+ {-# INLINE toJSON #-}
293+
294+instance FromJSON Int64 where
295+ fromJSON (Number n) = pure (floor n)
296+ fromJSON _ = empty
297+ {-# INLINE fromJSON #-}
298+
299+instance ToJSON Word where
300+ toJSON = Number . fromIntegral
301+ {-# INLINE toJSON #-}
302+
303+instance FromJSON Word where
304+ fromJSON (Number n) = pure (floor n)
305+ fromJSON _ = empty
306+ {-# INLINE fromJSON #-}
307+
308+instance ToJSON Word8 where
309+ toJSON = Number . fromIntegral
310+ {-# INLINE toJSON #-}
311+
312+instance FromJSON Word8 where
313+ fromJSON (Number n) = pure (floor n)
314+ fromJSON _ = empty
315+ {-# INLINE fromJSON #-}
316+
317+instance ToJSON Word16 where
318+ toJSON = Number . fromIntegral
319+ {-# INLINE toJSON #-}
320+
321+instance FromJSON Word16 where
322+ fromJSON (Number n) = pure (floor n)
323+ fromJSON _ = empty
324+ {-# INLINE fromJSON #-}
325+
326+instance ToJSON Word32 where
327+ toJSON = Number . fromIntegral
328+ {-# INLINE toJSON #-}
329+
330+instance FromJSON Word32 where
331+ fromJSON (Number n) = pure (floor n)
332+ fromJSON _ = empty
333+ {-# INLINE fromJSON #-}
334+
335+instance ToJSON Word64 where
336+ toJSON = Number . fromIntegral
337+ {-# INLINE toJSON #-}
338+
339+instance FromJSON Word64 where
340+ fromJSON (Number n) = pure (floor n)
341+ fromJSON _ = empty
342+ {-# INLINE fromJSON #-}
343+
215344instance ToJSON Text where
216345 toJSON = String
217346 {-# INLINE toJSON #-}
@@ -274,6 +403,14 @@ instance (Ord a, FromJSON a) => FromJSON (Set.Set a) where
274403 fromJSON = fmap Set.fromList . fromJSON
275404 {-# INLINE fromJSON #-}
276405
406+instance ToJSON IntSet.IntSet where
407+ toJSON = toJSON . IntSet.toList
408+ {-# INLINE toJSON #-}
409+
410+instance FromJSON IntSet.IntSet where
411+ fromJSON = fmap IntSet.fromList . fromJSON
412+ {-# INLINE fromJSON #-}
413+
277414instance (ToJSON v) => ToJSON (M.Map Text v) where
278415 toJSON = Object . M.map toJSON
279416 {-# INLINE toJSON #-}
@@ -354,12 +491,6 @@ instance FromJSON a => FromJSON (Last a) where
354491 fromJSON = fmap Last . fromJSON
355492 {-# INLINE fromJSON #-}
356493
357−-- | Transform one map into another. The ordering of keys must be
358−-- preserved.
359−transformMap :: (Ord k1, Ord k2) => (k1 -> k2) -> (v1 -> v2)
360− -> M.Map k1 v1 -> M.Map k2 v2
361−transformMap fk fv = M.fromAscList . map (fk *** fv) . M.toAscList
362−
363494mapA :: (Alternative m) => (t -> m a) -> [t] -> m [a]
364495mapA f = go
365496 where
aeson.cabal+5
@@ -64,9 +64,13 @@ library
6464 exposed-modules:
6565 Data.Aeson
6666 Data.Aeson.Encode
67+ Data.Aeson.Generic
6768 Data.Aeson.Parser
6869 Data.Aeson.Types
6970
71+ other-modules:
72+ Data.Aeson.Functions
73+
7074 build-depends:
7175 attoparsec >= 0.8.4.0,
7276 base == 4.*,
@@ -75,6 +79,7 @@ library
7579 containers,
7680 deepseq,
7781 old-locale,
82+ syb,
7883 text >= 0.11.0.2,
7984 time,
8085 vector >= 0.7