on master help sign in
Documentation
commita9bfcd3f05309d9a84419ffe6960c937370a6128 authorBryan O'Sullivan <bos@serpentine.com> dateTue 25 Jan 2011, 22:56 (16 years ago) parent3fbe8da
5 files changed+126−20
Data/Aeson.hs+19−2 Data/Aeson/Encode.hs+23−12 Data/Aeson/Parser.hs+13 Data/Aeson/Types.hs+70−5 benchmarks/ParseFile.hs+1−1
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+
110module Data.Aeson
211 (
3− Array
12+ -- * Core JSON types
13+ Value(..)
14+ , Array
415 , Object
5− , Value(..)
16+ -- * Type conversion
617 , FromJSON(..)
718 , ToJSON(..)
19+ -- * Constructors and accessors
820 , (.=)
921 , (.:)
1022 , object
23+ -- * Encoding and parsing
24+ , encode
25+ , json
1126 ) where
1227
28+import Data.Aeson.Encode
29+import Data.Aeson.Parser
1330import Data.Aeson.Types
Data/Aeson/Encode.hs+23−12
@@ -1,8 +1,17 @@
11{-# LANGUAGE OverloadedStrings #-}
22
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+
312module Data.Aeson.Encode
413 (
5− build
14+ fromValue
615 , encode
716 ) where
817
@@ -17,25 +26,26 @@ import qualified Data.Map as M
1726import qualified Data.Text as T
1827import qualified Data.Vector as V
1928
20−build :: Value -> Builder
21−build Null = fromByteString "null"
22−build (Bool b) = fromByteString $ if b then "true" else "false"
23−build (Number n) = fromByteString (B.pack (show n))
24−build (String s) = string s
25−build (Array v)
29+-- | Encode a JSON value to a 'Builder'.
30+fromValue :: Value -> Builder
31+fromValue Null = fromByteString "null"
32+fromValue (Bool b) = fromByteString $ if b then "true" else "false"
33+fromValue (Number n) = fromByteString (B.pack (show n))
34+fromValue (String s) = string s
35+fromValue (Array v)
2636 | V.null v = fromByteString "[]"
2737 | otherwise = fromChar '[' `mappend`
28− build (V.unsafeHead v) `mappend`
38+ fromValue (V.unsafeHead v) `mappend`
2939 V.foldr f (fromChar ']') (V.unsafeTail v)
30− where f a z = fromChar ',' `mappend` build a `mappend` z
31−build (Object m) =
40+ where f a z = fromChar ',' `mappend` fromValue a `mappend` z
41+fromValue (Object m) =
3242 case M.toList m of
3343 (x:xs) -> fromChar '{' `mappend`
3444 one x `mappend`
3545 foldr f (fromChar '}') xs
3646 _ -> fromByteString "{}"
3747 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
3949
4050string :: T.Text -> Builder
4151string s = fromChar '"' `mappend` quote s `mappend` fromChar '"'
@@ -55,6 +65,7 @@ string s = fromChar '"' `mappend` quote s `mappend` fromChar '"'
5565 | otherwise = fromChar c
5666 where h = showHex (fromEnum c) ""
5767
68+-- | Efficiently serialize a JSON value as a lazy 'L.ByteString'.
5869encode :: ToJSON a => a -> L.ByteString
59−encode = toLazyByteString . build . toJSON
70+encode = toLazyByteString . fromValue . toJSON
6071{-# INLINE encode #-}
Data/Aeson/Parser.hs+13
@@ -1,5 +1,14 @@
11{-# LANGUAGE OverloadedStrings #-}
22
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+
312module Data.Aeson.Parser
413 (
514 json
@@ -19,6 +28,8 @@ import Data.Vector as Vector hiding ((++))
1928import Data.Word (Word8)
2029import qualified Data.Attoparsec as A
2130
31+-- | Parse a top-level JSON value. This must be either an object or
32+-- an array.
2233json :: Parser Value
2334json = do
2435 c <- skipSpace *> anyChar
@@ -38,6 +49,8 @@ array_ = do
3849 vals <- ((value <* skipSpace) `sepBy` (char8 ',' *> skipSpace)) <* char8 ']'
3950 return . Array $ Vector.fromList vals
4051
52+-- | Parse any JSON value. Use 'json' in preference to this function
53+-- if you are parsing data from an untrusted source.
4154value :: Parser Value
4255value = most <|> (Number <$> double)
4356 where
Data/Aeson/Types.hs+70−5
@@ -1,19 +1,31 @@
11{-# LANGUAGE DeriveDataTypeable #-}
22
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+
312module Data.Aeson.Types
413 (
5− Array
14+ -- * Core JSON types
15+ Value(..)
16+ , Array
617 , Object
18+ -- * Type conversion
19+ , FromJSON(..)
20+ , ToJSON(..)
21+ -- * Constructors and accessors
722 , (.=)
823 , (.:)
924 , object
10− , Value(..)
11− , FromJSON(..)
12− , ToJSON(..)
1325 ) where
1426
1527import Control.Applicative
16−import Control.DeepSeq
28+import Control.DeepSeq (NFData(..))
1729import Data.Map (Map)
1830import Data.Text (Text, pack, unpack)
1931import Data.Time.Clock (UTCTime)
@@ -27,6 +39,7 @@ import qualified Data.Vector as V
2739type Object = Map Text Value
2840type Array = Vector Value
2941
42+-- | A JSON value represented as a Haskell value.
3043data Value = Object Object
3144 | Array Array
3245 | String Text
@@ -43,23 +56,57 @@ instance NFData Value where
4356 rnf (Bool b) = rnf b
4457 rnf Null = ()
4558
59+-- | Construct an 'Object' from a key and a value.
4660(.=) :: ToJSON a => Text -> a -> Object
4761name .= value = M.singleton name (toJSON value)
4862{-# INLINE (.=) #-}
4963
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..
5067(.:) :: (Alternative f, FromJSON a) => Object -> Text -> f a
5168obj .: key = case M.lookup key obj of
5269 Nothing -> empty
5370 Just v -> fromJSON v
5471{-# INLINE (.:) #-}
5572
73+-- | Create a 'Value' from a list of 'Object's. If duplicate
74+-- keys arise, earlier keys and their associated values win.
5675object :: [Object] -> Value
5776object = Object . M.unions
5877{-# INLINE object #-}
5978
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+-- @
6088class ToJSON a where
6189 toJSON :: a -> Value
6290
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+-- @
63110class FromJSON a where
64111 fromJSON :: Alternative f => Value -> f a
65112
@@ -73,6 +120,15 @@ instance (FromJSON a) => FromJSON (Maybe a) where
73120 fromJSON a = Just <$> fromJSON a
74121 {-# INLINE fromJSON #-}
75122
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+
76132instance ToJSON Bool where
77133 toJSON = Bool
78134 {-# INLINE toJSON #-}
@@ -100,6 +156,15 @@ instance FromJSON Int where
100156 fromJSON _ = empty
101157 {-# INLINE fromJSON #-}
102158
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+
103168instance ToJSON Text where
104169 toJSON = String
105170 {-# INLINE toJSON #-}
benchmarks/ParseFile.hs+1−1
@@ -24,7 +24,7 @@ main = do
2424 let refill = B.hGet h 1024
2525 result <- parseWith refill json =<< refill
2626 case result of
27− Done _ r -> rnf r `seq` loop (good+1) bad
27+ Done _ r -> loop (good+1) bad
2828 _ -> loop good (bad+1)
2929 (good, _) <- loop 0 0
3030 end <- getCurrentTime