on master help sign in
Start working on release notes and expanded examples.
eb3e4d3Bryan O'Sullivan
Data/Aeson/Generic.hs+27−4
@@ -16,14 +16,18 @@
16
16
17module Data.Aeson.Generic
17module Data.Aeson.Generic
18 (
18 (
19+ -- * Decoding and encoding
20+ decode
21+ , encode
22+ -- * Lower-level conversion functions
19− fromJSON
23+ , fromJSON
20 , toJSON
24 , toJSON
21 ) where
25 ) where
22
26
23import Control.Applicative ((<$>))
27import Control.Applicative ((<$>))
24import Control.Arrow (first)
28import Control.Arrow (first)
25import Control.Monad.State.Strict
29import Control.Monad.State.Strict
26−import Data.Aeson.Functions
30+import Data.Aeson.Functions hiding (decode)
27import Data.Aeson.Types hiding (FromJSON(..), ToJSON(..), fromJSON)
31import Data.Aeson.Types hiding (FromJSON(..), ToJSON(..), fromJSON)
28import Data.Attoparsec.Number (Number)
32import Data.Attoparsec.Number (Number)
29import Data.Generics
33import Data.Generics
@@ -35,7 +39,11 @@ import Data.Text (Text, pack, unpack)
35import Data.Text.Encoding (encodeUtf8)
39import Data.Text.Encoding (encodeUtf8)
36import Data.Time.Clock (UTCTime)
40import Data.Time.Clock (UTCTime)
37import Data.Word (Word, Word8, Word16, Word32, Word64)
41import Data.Word (Word, Word8, Word16, Word32, Word64)
42+import Data.Aeson.Parser (json)
43+import qualified Data.Aeson.Encode as E
44+import qualified Data.Aeson.Functions as F
38import qualified Data.Aeson.Types as T
45import qualified Data.Aeson.Types as T
46+import qualified Data.Attoparsec.Lazy as L
39import qualified Data.ByteString as B
47import qualified Data.ByteString as B
40import qualified Data.ByteString.Lazy as L
48import qualified Data.ByteString.Lazy as L
41import qualified Data.HashMap.Strict as H
49import qualified Data.HashMap.Strict as H
@@ -46,6 +54,21 @@ import qualified Data.Text.Lazy as LT
46import qualified Data.Traversable as T
54import qualified Data.Traversable as T
47import qualified Data.Vector as V
55import qualified Data.Vector as V
48
56
57+-- | Efficiently serialize a JSON value as a lazy 'L.ByteString'.
58+encode :: (Data a) => a -> L.ByteString
59+encode = E.encode . toJSON
60+{-# INLINE encode #-}
61+
62+-- | Efficiently deserialize a JSON value from a lazy 'L.ByteString'.
63+-- If this fails due to incomplete or invalid input, 'Nothing' is
64+-- returned.
65+decode :: (Data a) => L.ByteString -> Maybe a
66+decode s = case L.parse json s of
67+ L.Done _ v -> case fromJSON v of
68+ Success a -> Just a
69+ _ -> Nothing
70+ _ -> Nothing
71+
49type T a = a -> Value
72type T a = a -> Value
50
73
51toJSON :: (Data a) => a -> Value
74toJSON :: (Data a) => a -> Value
@@ -93,7 +116,7 @@ toJSON = toJSON_generic
93 | tyrep == typeOf DT.empty = remap id
116 | tyrep == typeOf DT.empty = remap id
94 | tyrep == typeOf LT.empty = remap LT.toStrict
117 | tyrep == typeOf LT.empty = remap LT.toStrict
95 | tyrep == typeOf "" = remap pack
118 | tyrep == typeOf "" = remap pack
96− | tyrep == typeOf B.empty = remap decode
119+ | tyrep == typeOf B.empty = remap F.decode
97 | tyrep == typeOf L.empty = remap strict
120 | tyrep == typeOf L.empty = remap strict
98 | otherwise = modError "toJSON" $
121 | otherwise = modError "toJSON" $
99 "cannot convert map keyed by type " ++ show tyrep
122 "cannot convert map keyed by type " ++ show tyrep
@@ -104,7 +127,7 @@ toJSON = toJSON_generic
104 | tyrep == typeOf DT.empty = remap id
127 | tyrep == typeOf DT.empty = remap id
105 | tyrep == typeOf LT.empty = remap LT.toStrict
128 | tyrep == typeOf LT.empty = remap LT.toStrict
106 | tyrep == typeOf "" = remap pack
129 | tyrep == typeOf "" = remap pack
107− | tyrep == typeOf B.empty = remap decode
130+ | tyrep == typeOf B.empty = remap F.decode
108 | tyrep == typeOf L.empty = remap strict
131 | tyrep == typeOf L.empty = remap strict
109 | otherwise = modError "toJSON" $
132 | otherwise = modError "toJSON" $
110 "cannot convert map keyed by type " ++ show tyrep
133 "cannot convert map keyed by type " ++ show tyrep
aeson.cabal+12−7
@@ -3,15 +3,16 @@ version: 0.4.0.0
3license: BSD3
3license: BSD3
4license-file: LICENSE
4license-file: LICENSE
5category: Text, Web, JSON
5category: Text, Web, JSON
6+copyright: (c) 2011 Bryan O'Sullivan
6−copyright: Copyright 2011 MailRank, Inc.
7+ (c) 2011 MailRank, Inc.
7author: Bryan O'Sullivan <bos@serpentine.com>
8author: Bryan O'Sullivan <bos@serpentine.com>
8maintainer: Bryan O'Sullivan <bos@serpentine.com>
9maintainer: Bryan O'Sullivan <bos@serpentine.com>
9stability: experimental
10stability: experimental
10−tested-with: GHC == 6.12.3
11+tested-with: GHC == 6.12.3, GHC == 7.0.4, GHC == 7.2.2
11synopsis: Fast JSON parsing and encoding
12synopsis: Fast JSON parsing and encoding
12cabal-version: >= 1.8
13cabal-version: >= 1.8
13−homepage: http://github.com/bos/aeson
14+homepage: https://github.com/bos/aeson
14−bug-reports: http://github.com/bos/aeson/issues
15+bug-reports: https://github.com/bos/aeson/issues
15build-type: Simple
16build-type: Simple
16description:
17description:
17 A JSON parsing and encoding library optimized for ease of use
18 A JSON parsing and encoding library optimized for ease of use
@@ -20,6 +21,9 @@ description:
20 To get started, see the documentation for the @Data.Aeson@ module
21 To get started, see the documentation for the @Data.Aeson@ module
21 below.
22 below.
22 .
23 .
24+ For release notes, see
25+ <https://github.com/bos/aeson/blob/master/release-notes.markdown>
26+ .
23 /Note/: if you use GHCi or Template Haskell, please see the
27 /Note/: if you use GHCi or Template Haskell, please see the
24 @README@ file for important details about building this package,
28 @README@ file for important details about building this package,
25 and other packages that depend on it:
29 and other packages that depend on it:
@@ -80,6 +84,7 @@ description:
80
84
81extra-source-files:
85extra-source-files:
82 README.markdown
86 README.markdown
87+ release-notes.markdown
83 benchmarks/AesonEncode.hs
88 benchmarks/AesonEncode.hs
84 benchmarks/AesonParse.hs
89 benchmarks/AesonParse.hs
85 benchmarks/JsonParse.hs
90 benchmarks/JsonParse.hs
@@ -98,7 +103,7 @@ extra-source-files:
98 benchmarks/json-data/twitter50.json
103 benchmarks/json-data/twitter50.json
99 benchmarks/json-data/twitter100.json
104 benchmarks/json-data/twitter100.json
100 tests/Properties.hs
105 tests/Properties.hs
101− examples/Demo.hs
106+ examples/*.hs
102
107
103flag developer
108flag developer
104 description: operate in developer mode
109 description: operate in developer mode
@@ -170,8 +175,8 @@ test-suite tests
170
175
171source-repository head
176source-repository head
172 type: git
177 type: git
173− location: http://github.com/bos/aeson
178+ location: git://github.com/bos/aeson.git
174
179
175source-repository head
180source-repository head
176 type: mercurial
181 type: mercurial
177− location: http://bitbucket.org/bos/aeson
182+ location: https://bitbucket.org/bos/aeson
examples/Generic.hsnew file+38
@@ -0,0 +1,38 @@
1+-- This example is basically the same as in Simplest.hs, only it uses
2+-- GHC's builtin generics instead of explicit instances of ToJSON and
3+-- FromJSON.
4+
5+-- This example only works with GHC 7.2 or newer, as it uses the
6+-- datatype-generic programming machinery introduced in 7.2.
7+
8+-- We enable the DeriveGeneric language extension so that GHC can
9+-- automatically derive the Generic class for us.
10+
11+{-# LANGUAGE DeriveGeneric #-}
12+
13+{-# LANGUAGE OverloadedStrings #-}
14+
15+import Data.Aeson (FromJSON, ToJSON, decode, encode)
16+import qualified Data.ByteString.Lazy.Char8 as BL
17+
18+-- To decode or encode a value using the generic machinery, we must
19+-- make the type an instance of the Generic class.
20+
21+import GHC.Generics (Generic)
22+
23+data Coord = Coord { x :: Double, y :: Double }
24+ deriving (Show, Generic)
25+
26+-- While we still have to declare our type as instances of FromJSON
27+-- and ToJSON, we do *not* need to provide bodies for the instances.
28+-- Default versions will be supplied for us.
29+
30+instance FromJSON Coord
31+instance ToJSON Coord
32+
33+main ::IO ()
34+main = do
35+ let req = decode "{\"x\":3.0,\"y\":-1.0}" :: Maybe Coord
36+ print req
37+ let reply = Coord 123.4 20
38+ BL.putStrLn (encode reply)
examples/GenericSYB.hsnew file+30
@@ -0,0 +1,30 @@
1+-- This example is basically the same as in Simplest.hs, only it uses
2+-- SYB generics instead of explicit instances of ToJSON and FromJSON.
3+
4+-- This mechanism is much slower than the newer generics mechanism
5+-- demonstrated in Generic.hs, but it works on versions of GHC older
6+-- than 7.2.
7+
8+-- We enable the DeriveDataTypeable language extension so that GHC can
9+-- automatically derive the Typeable and Data classes for us.
10+
11+{-# LANGUAGE DeriveDataTypeable #-}
12+
13+{-# LANGUAGE OverloadedStrings #-}
14+
15+import Data.Data (Typeable, Data)
16+import Data.Aeson.Generic (decode, encode)
17+import qualified Data.ByteString.Lazy.Char8 as BL
18+
19+-- To decode or encode a value using the generic machinery, we must
20+-- make the type an instance of the Typeable and Data classes.
21+
22+data Coord = Coord { x :: Double, y :: Double }
23+ deriving (Show, Typeable, Data)
24+
25+main ::IO ()
26+main = do
27+ let req = decode "{\"x\":3.0,\"y\":-1.0}" :: Maybe Coord
28+ print req
29+ let reply = Coord 123.4 20
30+ BL.putStrLn (encode reply)
examples/Simplest.hs+19−22
@@ -1,33 +1,30 @@
1{-# LANGUAGE OverloadedStrings #-}
1{-# LANGUAGE OverloadedStrings #-}
2
2
3−import Control.Applicative ((<$>), (<*>))
3+import Control.Applicative ((<$>), (<*>), empty)
4−import Control.Monad (mzero)
5import Data.Aeson
4import Data.Aeson
6−import qualified Data.Aeson.Types as T
5+import qualified Data.ByteString.Lazy.Char8 as BL
7−import Data.Attoparsec (parse, Result(..))
8−import qualified Data.ByteString.Char8 as BS
9−import qualified Data.ByteString.Lazy.Char8 as BSL
10
6
11−main ::IO ()
7+data Coord = Coord { x :: Double, y :: Double }
12−main = do
8+ deriving (Show)
13− print $ parseFromString "{\"x\":3.0,\"y\":-1.0}"
14− let reply = Coord 123.4 20
15− putStrLn $ BSL.unpack (encode reply)
16
9
17−data Coord = Coord { x :: Double, y :: Double } deriving (Show)
10+-- A ToJSON instance allows us to encode a value as JSON.
18
11
19instance ToJSON Coord where
12instance ToJSON Coord where
20− toJSON (Coord xV yV) = object ["x" .= xV, "y" .= yV]
13+ toJSON (Coord xV yV) = object [ "x" .= xV,
14+ "y" .= yV ]
15+
16+-- A FromJSON instance allows us to decode a value from JSON. This
17+-- should match the format used by the ToJSON instance.
21
18
22instance FromJSON Coord where
19instance FromJSON Coord where
23 parseJSON (Object v) = Coord <$>
20 parseJSON (Object v) = Coord <$>
24− v .: "x" <*>
21+ v .: "x" <*>
25− v .: "y"
22+ v .: "y"
26− parseJSON _ = mzero
23+ parseJSON _ = empty
27
24
28−parseFromString :: String -> Maybe Coord
25+main ::IO ()
29−parseFromString s =
26+main = do
30− let bs = BS.pack s
27+ let req = decode "{\"x\":3.0,\"y\":-1.0}" :: Maybe Coord
31− in case parse json bs of
28+ print req
32− Done _rest res -> T.parseMaybe parseJSON res
29+ let reply = Coord 123.4 20
33− _ -> Nothing
30+ BL.putStrLn (encode reply)
release-notes.markdownnew file+27
@@ -0,0 +1,27 @@
1+# 0.3 to 0.4
2+
3+## Generics support
4+
5+Aeson's support for data-type generic programming makes it possible to
6+use JSON encodings of most data types without writing any boilerplate
7+instances.
8+
9+Thanks to Bas Van Dijk, aeson now supports the two major schemes for
10+doing datatype-generic programming:
11+
12+* the newer mechanism, [built into GHC
13+ itself](http://www.haskell.org/ghc/docs/latest/html/users_guide/generic-programming.html)
14+
15+* the older mechanism, based on SYB (for "scrap your boilerplate")
16+
17+The GHC-based generics are fast and terse: in fact, they're generally
18+comparable in performance to hand-written `ToJSON` and `FromJSON`
19+instances. To see how to use GHC generics, see
20+[`examples/Generic.hs`](https://github.com/bos/aeson/blob/master/examples/Generic.hs).
21+
22+The SYB-based generics support lives in
23+[Data.Aeson.Generic](http://hackage.haskell.org/packages/archive/aeson/0.4.0.0/doc/html/Data-Aeson-Generic.html),
24+and is provided mainly for users of GHC older than 7.2. It's far
25+slower (by about 10x) than the more modern generic mechanism. To see
26+how to use SYB generics, see
27+[`examples/GenericSYB.hs`](https://github.com/bos/aeson/blob/master/examples/GenericSYB.hs).