on master help sign in
Documentation
a9bfcd3Bryan O'Sullivan
Data/Aeson.hs+19−2
@@ -1,13 +1,30 @@
1+-- Module: Data.Aeson
2+-- Copyright: (c) 2011 MailRank, Inc.
3+-- License: Apache
4+-- Maintainer: Bryan O'Sullivan <bos@mailrank.com>
5+-- Stability: experimental
6+-- Portability: portable
7+--
8+-- Types and functions for working efficiently with JSON data.
9+
1module Data.Aeson
10module Data.Aeson
2 (
11 (
12+ -- * Core JSON types
13+ Value(..)
3− Array
14+ , Array
4 , Object
15 , Object
5− , Value(..)
16+ -- * Type conversion
6 , FromJSON(..)
17 , FromJSON(..)
7 , ToJSON(..)
18 , ToJSON(..)
19+ -- * Constructors and accessors
8 , (.=)
20 , (.=)
9 , (.:)
21 , (.:)
10 , object
22 , object
23+ -- * Encoding and parsing
24+ , encode
25+ , json
11 ) where
26 ) where
12
27
28+import Data.Aeson.Encode
29+import Data.Aeson.Parser
13import Data.Aeson.Types
30import Data.Aeson.Types
Data/Aeson/Encode.hs+23−12
@@ -1,8 +1,17 @@
1{-# LANGUAGE OverloadedStrings #-}
1{-# LANGUAGE OverloadedStrings #-}
2
2
3+-- Module: Data.Aeson.Encode
4+-- Copyright: (c) 2011 MailRank, Inc.
5+-- License: Apache
6+-- Maintainer: Bryan O'Sullivan <bos@mailrank.com>
7+-- Stability: experimental
8+-- Portability: portable
9+--
10+-- Efficiently serialize a JSON value as a lazy 'L.ByteString'.
11+
3module Data.Aeson.Encode
12module Data.Aeson.Encode
4 (
13 (
5− build
14+ fromValue
6 , encode
15 , encode
7 ) where
16 ) where
8
17
@@ -17,25 +26,26 @@ import qualified Data.Map as M
17import qualified Data.Text as T
26import qualified Data.Text as T
18import qualified Data.Vector as V
27import qualified Data.Vector as V
19
28
29+-- | Encode a JSON value to a 'Builder'.
20−build :: Value -> Builder
30+fromValue :: Value -> Builder
21−build Null = fromByteString "null"
31+fromValue Null = fromByteString "null"
22−build (Bool b) = fromByteString $ if b then "true" else "false"
32+fromValue (Bool b) = fromByteString $ if b then "true" else "false"
23−build (Number n) = fromByteString (B.pack (show n))
33+fromValue (Number n) = fromByteString (B.pack (show n))
24−build (String s) = string s
34+fromValue (String s) = string s
25−build (Array v)
35+fromValue (Array v)
26 | V.null v = fromByteString "[]"
36 | V.null v = fromByteString "[]"
27 | otherwise = fromChar '[' `mappend`
37 | otherwise = fromChar '[' `mappend`
28− build (V.unsafeHead v) `mappend`
38+ fromValue (V.unsafeHead v) `mappend`
29 V.foldr f (fromChar ']') (V.unsafeTail v)
39 V.foldr f (fromChar ']') (V.unsafeTail v)
30− where f a z = fromChar ',' `mappend` build a `mappend` z
40+ where f a z = fromChar ',' `mappend` fromValue a `mappend` z
31−build (Object m) =
41+fromValue (Object m) =
32 case M.toList m of
42 case M.toList m of
33 (x:xs) -> fromChar '{' `mappend`
43 (x:xs) -> fromChar '{' `mappend`
34 one x `mappend`
44 one x `mappend`
35 foldr f (fromChar '}') xs
45 foldr f (fromChar '}') xs
36 _ -> fromByteString "{}"
46 _ -> fromByteString "{}"
37 where f a z = fromChar ',' `mappend` one a `mappend` z
47 where f a z = fromChar ',' `mappend` one a `mappend` z
38− one (k,v) = string k `mappend` fromChar ':' `mappend` build v
48+ one (k,v) = string k `mappend` fromChar ':' `mappend` fromValue v
39
49
40string :: T.Text -> Builder
50string :: T.Text -> Builder
41string s = fromChar '"' `mappend` quote s `mappend` fromChar '"'
51string s = fromChar '"' `mappend` quote s `mappend` fromChar '"'
@@ -55,6 +65,7 @@ string s = fromChar '"' `mappend` quote s `mappend` fromChar '"'
55 | otherwise = fromChar c
65 | otherwise = fromChar c
56 where h = showHex (fromEnum c) ""
66 where h = showHex (fromEnum c) ""
57
67
68+-- | Efficiently serialize a JSON value as a lazy 'L.ByteString'.
58encode :: ToJSON a => a -> L.ByteString
69encode :: ToJSON a => a -> L.ByteString
59−encode = toLazyByteString . build . toJSON
70+encode = toLazyByteString . fromValue . toJSON
60{-# INLINE encode #-}
71{-# INLINE encode #-}
Data/Aeson/Parser.hs+13
@@ -1,5 +1,14 @@
1{-# LANGUAGE OverloadedStrings #-}
1{-# LANGUAGE OverloadedStrings #-}
2
2
3+-- Module: Data.Aeson.Parser
4+-- Copyright: (c) 2011 MailRank, Inc.
5+-- License: Apache
6+-- Maintainer: Bryan O'Sullivan <bos@mailrank.com>
7+-- Stability: experimental
8+-- Portability: portable
9+--
10+-- Efficiently and correctly parse a JSON string.
11+
3module Data.Aeson.Parser
12module Data.Aeson.Parser
4 (
13 (
5 json
14 json
@@ -19,6 +28,8 @@ import Data.Vector as Vector hiding ((++))
19import Data.Word (Word8)
28import Data.Word (Word8)
20import qualified Data.Attoparsec as A
29import qualified Data.Attoparsec as A
21
30
31+-- | Parse a top-level JSON value. This must be either an object or
32+-- an array.
22json :: Parser Value
33json :: Parser Value
23json = do
34json = do
24 c <- skipSpace *> anyChar
35 c <- skipSpace *> anyChar
@@ -38,6 +49,8 @@ array_ = do
38 vals <- ((value <* skipSpace) `sepBy` (char8 ',' *> skipSpace)) <* char8 ']'
49 vals <- ((value <* skipSpace) `sepBy` (char8 ',' *> skipSpace)) <* char8 ']'
39 return . Array $ Vector.fromList vals
50 return . Array $ Vector.fromList vals
40
51
52+-- | Parse any JSON value. Use 'json' in preference to this function
53+-- if you are parsing data from an untrusted source.
41value :: Parser Value
54value :: Parser Value
42value = most <|> (Number <$> double)
55value = most <|> (Number <$> double)
43 where
56 where
Data/Aeson/Types.hs+70−5
@@ -1,19 +1,31 @@
1{-# LANGUAGE DeriveDataTypeable #-}
1{-# LANGUAGE DeriveDataTypeable #-}
2
2
3+-- Module: Data.Aeson.Types
4+-- Copyright: (c) 2011 MailRank, Inc.
5+-- License: Apache
6+-- Maintainer: Bryan O'Sullivan <bos@mailrank.com>
7+-- Stability: experimental
8+-- Portability: portable
9+--
10+-- Types for working with JSON data.
11+
3module Data.Aeson.Types
12module Data.Aeson.Types
4 (
13 (
14+ -- * Core JSON types
15+ Value(..)
5− Array
16+ , Array
6 , Object
17 , Object
18+ -- * Type conversion
19+ , FromJSON(..)
20+ , ToJSON(..)
21+ -- * Constructors and accessors
7 , (.=)
22 , (.=)
8 , (.:)
23 , (.:)
9 , object
24 , object
10− , Value(..)
11− , FromJSON(..)
12− , ToJSON(..)
13 ) where
25 ) where
14
26
15import Control.Applicative
27import Control.Applicative
16−import Control.DeepSeq
28+import Control.DeepSeq (NFData(..))
17import Data.Map (Map)
29import Data.Map (Map)
18import Data.Text (Text, pack, unpack)
30import Data.Text (Text, pack, unpack)
19import Data.Time.Clock (UTCTime)
31import Data.Time.Clock (UTCTime)
@@ -27,6 +39,7 @@ import qualified Data.Vector as V
27type Object = Map Text Value
39type Object = Map Text Value
28type Array = Vector Value
40type Array = Vector Value
29
41
42+-- | A JSON value represented as a Haskell value.
30data Value = Object Object
43data Value = Object Object
31 | Array Array
44 | Array Array
32 | String Text
45 | String Text
@@ -43,23 +56,57 @@ instance NFData Value where
43 rnf (Bool b) = rnf b
56 rnf (Bool b) = rnf b
44 rnf Null = ()
57 rnf Null = ()
45
58
59+-- | Construct an 'Object' from a key and a value.
46(.=) :: ToJSON a => Text -> a -> Object
60(.=) :: ToJSON a => Text -> a -> Object
47name .= value = M.singleton name (toJSON value)
61name .= value = M.singleton name (toJSON value)
48{-# INLINE (.=) #-}
62{-# INLINE (.=) #-}
49
63
64+-- | Retrieve the value associated with the given key of an 'Object'.
65+-- The result is 'empty' if the key is not present or cannot be
66+-- converted to the desired type..
50(.:) :: (Alternative f, FromJSON a) => Object -> Text -> f a
67(.:) :: (Alternative f, FromJSON a) => Object -> Text -> f a
51obj .: key = case M.lookup key obj of
68obj .: key = case M.lookup key obj of
52 Nothing -> empty
69 Nothing -> empty
53 Just v -> fromJSON v
70 Just v -> fromJSON v
54{-# INLINE (.:) #-}
71{-# INLINE (.:) #-}
55
72
73+-- | Create a 'Value' from a list of 'Object's. If duplicate
74+-- keys arise, earlier keys and their associated values win.
56object :: [Object] -> Value
75object :: [Object] -> Value
57object = Object . M.unions
76object = Object . M.unions
58{-# INLINE object #-}
77{-# INLINE object #-}
59
78
79+-- | A type that can be converted to JSON.
80+--
81+-- An example type and instance:
82+--
83+-- @data Coord { x :: Double, y :: Double }
84+--
85+-- instance ToJSON Coord where
86+-- toJSON (Coord x y) = 'object' [\"x\" '.=' x, \"y\" '.=' y]
87+-- @
60class ToJSON a where
88class ToJSON a where
61 toJSON :: a -> Value
89 toJSON :: a -> Value
62
90
91+-- | A type that can be converted from JSON, with the possibility of
92+-- failure.
93+--
94+-- When writing an instance, use 'empty' to make a conversion fail,
95+-- e.g. if an 'Object' is missing a required key, or the value is of
96+-- the wrong type.
97+--
98+-- An example type and instance:
99+--
100+-- @data Coord { x :: Double, y :: Double }
101+--
102+-- instance FromJSON Coord where
103+-- fromJSON ('Object' v) = Coord '<$>'
104+-- v '.:' \"x\" '<*>'
105+-- v '.:' \"y\"
106+--
107+-- \-- A non-'Object' value is of the wrong type, so use 'empty' to fail.
108+-- fromJSON _ = 'empty'
109+-- @
63class FromJSON a where
110class FromJSON a where
64 fromJSON :: Alternative f => Value -> f a
111 fromJSON :: Alternative f => Value -> f a
65
112
@@ -73,6 +120,15 @@ instance (FromJSON a) => FromJSON (Maybe a) where
73 fromJSON a = Just <$> fromJSON a
120 fromJSON a = Just <$> fromJSON a
74 {-# INLINE fromJSON #-}
121 {-# INLINE fromJSON #-}
75
122
123+instance (ToJSON a, ToJSON b) => ToJSON (Either a b) where
124+ toJSON (Left a) = toJSON a
125+ toJSON (Right b) = toJSON b
126+ {-# INLINE toJSON #-}
127+
128+instance (FromJSON a, FromJSON b) => FromJSON (Either a b) where
129+ fromJSON a = Left <$> fromJSON a <|> Right <$> fromJSON a
130+ {-# INLINE fromJSON #-}
131+
76instance ToJSON Bool where
132instance ToJSON Bool where
77 toJSON = Bool
133 toJSON = Bool
78 {-# INLINE toJSON #-}
134 {-# INLINE toJSON #-}
@@ -100,6 +156,15 @@ instance FromJSON Int where
100 fromJSON _ = empty
156 fromJSON _ = empty
101 {-# INLINE fromJSON #-}
157 {-# INLINE fromJSON #-}
102
158
159+instance ToJSON Integer where
160+ toJSON = Number . fromIntegral
161+ {-# INLINE toJSON #-}
162+
163+instance FromJSON Integer where
164+ fromJSON (Number n) = pure (floor n)
165+ fromJSON _ = empty
166+ {-# INLINE fromJSON #-}
167+
103instance ToJSON Text where
168instance ToJSON Text where
104 toJSON = String
169 toJSON = String
105 {-# INLINE toJSON #-}
170 {-# INLINE toJSON #-}
benchmarks/ParseFile.hs+1−1
@@ -24,7 +24,7 @@ main = do
24 let refill = B.hGet h 1024
24 let refill = B.hGet h 1024
25 result <- parseWith refill json =<< refill
25 result <- parseWith refill json =<< refill
26 case result of
26 case result of
27− Done _ r -> rnf r `seq` loop (good+1) bad
27+ Done _ r -> loop (good+1) bad
28 _ -> loop good (bad+1)
28 _ -> loop good (bad+1)
29 (good, _) <- loop 0 0
29 (good, _) <- loop 0 0
30 end <- getCurrentTime
30 end <- getCurrentTime