on master help sign in
More doc improvements
commit176fbb38ffb2e672652fa771a21fe4bbde28c883 authorBryan O'Sullivan <bos@serpentine.com> dateMon 20 Jul 2015, 23:04 (11 years ago) parent7c3a686
3 files changed+143−46
Data/Aeson.hs+72−12 Data/Aeson/Types/Class.hs+67−31 Data/Aeson/Types/Internal.hs+4−3
Data/Aeson.hs+72−12
@@ -30,6 +30,9 @@ module Data.Aeson
3030
3131 -- * Encoding and decoding
3232 -- $encoding_and_decoding
33+
34+ -- ** Direct encoding
35+ -- $encoding
3336 decode
3437 , decode'
3538 , eitherDecode
@@ -54,12 +57,13 @@ module Data.Aeson
5457 , fromJSON
5558 , ToJSON(..)
5659 , KeyValue(..)
57− -- ** Generic JSON classes
60+ -- ** Generic JSON classes and options
5861 , GFromJSON(..)
5962 , GToJSON(..)
6063 , genericToJSON
6164 , genericToEncoding
6265 , genericParseJSON
66+ , defaultOptions
6367
6468 -- * Inspecting @'Value's@
6569 , withObject
@@ -199,8 +203,17 @@ eitherDecodeStrict' =
199203-- 'FromJSON' and 'ToJSON' instances for which the compiler will
200204-- generate sensible default implementations.
201205--
202−-- > instance ToJSON Person
203−-- > instance FromJSON Person
206+-- @
207+-- instance 'ToJSON' Person where
208+-- \-- No need to provide a 'toJSON' implementation.
209+--
210+-- \-- For efficiency, we write a simple 'toEncoding' implementation, as
211+-- \-- the default version uses 'toJSON'.
212+-- 'toEncoding' = 'genericToEncoding' 'defaultOptions'
213+--
214+-- instance 'FromJSON' Person
215+-- \-- No need to provide a 'parseJSON' implementation.
216+-- @
204217--
205218-- We can now encode a value like so:
206219--
@@ -240,16 +253,19 @@ eitherDecodeStrict' =
240253-- > >>> decode "{\"name\":\"Joe\",\"age\":12}" :: Maybe Person
241254-- > Just (Person {name = "Joe", age = 12})
242255--
243−-- To encode data, we need to define a 'ToJSON' instance:
256+-- To encode data, we need to define a 'ToJSON' instance. Let's begin
257+-- with an instance written entirely by hand.
244258--
245−-- > instance ToJSON Person where
246−-- > -- this generates a Value
247−-- > toJSON (Person name age) =
248−-- > object ["name" .= name, "age" .= age]
249−-- >
250−-- > -- this encodes directly to a ByteString Builder
251−-- > toEncoding (Person name age) =
252−-- > pairs $ "name" .= name <> "age" .= age
259+-- @
260+-- instance ToJSON Person where
261+-- \-- this generates a 'Value'
262+-- 'toJSON' (Person name age) =
263+-- 'object' [\"name\" '.=' name, \"age\" '.=' age]
264+--
265+-- \-- this encodes directly to a bytestring Builder
266+-- 'toEncoding' (Person name age) =
267+-- 'pairs' (\"name\" '.=' 'name' '<>' \"age\" '.=' age)
268+-- @
253269--
254270-- We can now encode a value like so:
255271--
@@ -363,3 +379,47 @@ eitherDecodeStrict' =
363379--
364380-- For convenience, the 'encode' and 'decode' functions combine both
365381-- steps.
382+
383+-- $encoding
384+--
385+-- In older versions of this library, encoding a Haskell value
386+-- involved converting to an intermediate 'Value', then encoding that.
387+--
388+-- A \"direct\" encoder converts straight from a source Haskell value
389+-- to a 'BL.ByteString' without constructing an intermediate 'Value'.
390+-- This approach is faster than 'toJSON', and allocates less memory.
391+-- The 'toEncoding' method makes it possible to implement direct
392+-- encoding with low memory overhead.
393+--
394+-- To complicate matters, the default implementation of 'toEncoding'
395+-- uses 'toJSON'. Why? The 'toEncoding' method was added to this
396+-- library much more recently than 'toJSON'. Using 'toJSON' ensures
397+-- that packages written against older versions of this library will
398+-- compile and produce correct output, but they will not see any
399+-- speedup from direct encoding.
400+--
401+-- To write a minimal implementation of direct encoding, your type
402+-- must implement GHC's 'Generic' class, and your code should look
403+-- like this:
404+--
405+-- @
406+-- 'toEncoding' = 'genericToEncoding' 'defaultOptions'
407+-- @
408+--
409+-- What if you have more elaborate encoding needs? For example,
410+-- perhaps you need to change the names of object keys, omit parts of
411+-- a value.
412+--
413+-- To encode to a JSON \"object\", use the 'pairs' function.
414+--
415+-- @
416+-- 'toEncoding' (Person name age) =
417+-- 'pairs' (\"name\" '.=' 'name' '<>' \"age\" '.=' age)
418+-- @
419+--
420+-- Any container type that implements 'Foldable' can be encoded to a
421+-- JSON \"array\" using 'foldable'.
422+--
423+-- > > import Data.Sequence as Seq
424+-- > > encode (Seq.fromList [1,2,3])
425+-- > "[1,2,3]"
Data/Aeson/Types/Class.hs+67−31
@@ -1,4 +1,4 @@
1−{-# LANGUAGE DefaultSignatures, FlexibleContexts #-}
1+{-# LANGUAGE CPP, DefaultSignatures, FlexibleContexts #-}
22
33-- |
44-- Module: Data.Aeson.Types.Class
@@ -72,45 +72,60 @@ genericParseJSON opts = fmap to . gParseJSON opts
7272--
7373-- An example type and instance:
7474--
75−-- @{-\# LANGUAGE OverloadedStrings #-}
75+-- @
76+-- \-- Allow ourselves to write 'Text' literals.
77+-- {-\# LANGUAGE OverloadedStrings #-}
7678--
7779-- data Coord = Coord { x :: Double, y :: Double }
7880--
7981-- instance ToJSON Coord where
8082-- toJSON (Coord x y) = 'object' [\"x\" '.=' x, \"y\" '.=' y]
81−-- @
8283--
83−-- Note the use of the @OverloadedStrings@ language extension which enables
84−-- 'Text' values to be written as string literals.
84+-- toEncoding (Coord x y) = 'pairs' (\"x\" '.=' x '<>' \"y\" '.=' y)
85+-- @
8586--
86−-- Instead of manually writing your 'ToJSON' instance, there are three options
87+-- Instead of manually writing your 'ToJSON' instance, there are two options
8788-- to do it automatically:
8889--
8990-- * "Data.Aeson.TH" provides Template Haskell functions which will derive an
9091-- instance at compile time. The generated instance is optimized for your type
9192-- so will probably be more efficient than the following two options:
9293--
93−-- * The compiler can provide default generic implementations for 'toJSON' and
94−-- 'toEncoding'.
94+-- * The compiler can provide a default generic implementation for
95+-- 'toJSON'.
9596--
9697-- To use the second, simply add a @deriving 'Generic'@ clause to your
9798-- datatype and declare a 'ToJSON' instance for your datatype without giving
9899-- definitions for 'toJSON' or 'toEncoding'.
99100--
100−-- For example, the previous example can be simplified to just:
101+-- For example, the previous example can be simplified to a more
102+-- minimal instance:
101103--
102−-- @{-\# LANGUAGE DeriveGeneric \#-}
104+-- @
105+-- {-\# LANGUAGE DeriveGeneric \#-}
103106--
104−-- import GHC.Generics
107+-- import "GHC.Generics"
105108--
106−-- data Coord = Coord { x :: Double, y :: Double } deriving Generic
109+-- data Coord = Coord { x :: Double, y :: Double } deriving 'Generic'
107110--
108−-- instance ToJSON Coord
111+-- instance ToJSON Coord where
112+-- toEncoding = 'genericToEncoding' 'defaultOptions'
109113-- @
110114--
111−-- Note that, instead of using @DefaultSignatures@, it's also possible
112−-- to parameterize the generic encoding using 'genericToJSON' applied
113−-- to your encoding/decoding 'Options':
115+-- Why do we provide an implementation for 'toEncoding' here? The
116+-- 'toEncoding' function is a relatively new addition to this class.
117+-- To allow users of older versions of this library to upgrade without
118+-- having to edit all of their instances or encounter surprising
119+-- incompatibilities, the default implementation of 'toEncoding' uses
120+-- 'toJSON'. This produces correct results, but since it performs an
121+-- intermediate conversion to a 'Value', it will be less efficient
122+-- than directly emitting an 'Encoding'. Our one-liner definition of
123+-- 'toEncoding' above bypasses the intermediate 'Value'.
124+--
125+-- If @DefaultSignatures@ doesn't give exactly the results you want,
126+-- you can customize the generic encoding with only a tiny amount of
127+-- effort, using 'genericToJSON' and 'genericToEncoding' with your
128+-- preferred 'Options':
114129--
115130-- @
116131-- instance ToJSON Coord where
@@ -118,11 +133,31 @@ genericParseJSON opts = fmap to . gParseJSON opts
118133-- toEncoding = 'genericToEncoding' 'defaultOptions'
119134-- @
120135class ToJSON a where
136+ -- | Convert a Haskell value to a JSON-friendly intermediate type.
121137 toJSON :: a -> Value
138+ {-# MINIMAL toJSON #-}
122139
123140 default toJSON :: (Generic a, GToJSON (Rep a)) => a -> Value
124141 toJSON = genericToJSON defaultOptions
125142
143+ -- | Encode a Haskell value as JSON.
144+ --
145+ -- The default implementation of this method creates an
146+ -- intermediate 'Value' using 'toJSON'. This provides
147+ -- source-level compatibility for people upgrading from older
148+ -- versions of this library, but obviously offers no performance
149+ -- advantage.
150+ --
151+ -- To benefit from direct encoding, you /must/ provide an
152+ -- implementation for this method. The easiest way to do so is by
153+ -- having your types implement 'Generic' using the @DeriveGeneric@
154+ -- extension, and then have GHC generate a method body as follows.
155+ --
156+ -- @
157+ -- instance ToJSON Coord where
158+ -- toEncoding = 'genericToEncoding' 'defaultOptions'
159+ -- @
160+
126161 toEncoding :: a -> Encoding
127162 toEncoding = Encoding . E.encodeToBuilder . toJSON
128163 {-# INLINE toEncoding #-}
@@ -130,6 +165,10 @@ class ToJSON a where
130165-- | A type that can be converted from JSON, with the possibility of
131166-- failure.
132167--
168+-- In many cases, you can get the compiler to generate parsing code
169+-- for you (see below). To begin, let's cover writing an instance by
170+-- hand.
171+--
133172-- There are various reasons a conversion could fail. For example, an
134173-- 'Object' could be missing a required key, an 'Array' could be of
135174-- the wrong size, or a value could be of an incompatible type.
@@ -138,7 +177,7 @@ class ToJSON a where
138177--
139178-- * 'empty' and 'mzero' work, but are terse and uninformative
140179--
141−-- * 'fail' produces a custom error message
180+-- * 'fail' yields a custom error message
142181--
143182-- * 'typeMismatch' produces an informative message for cases when the
144183-- value encountered is not of the expected type
@@ -146,6 +185,7 @@ class ToJSON a where
146185-- An example type and instance:
147186--
148187-- @
188+-- \-- Allow ourselves to write 'Text' literals.
149189-- {-\# LANGUAGE OverloadedStrings #-}
150190--
151191-- data Coord = Coord { x :: Double, y :: Double }
@@ -155,15 +195,12 @@ class ToJSON a where
155195-- v '.:' \"x\" '<*>'
156196-- v '.:' \"y\"
157197--
158−-- \-- A non-'Object' value is of the wrong type.
159−-- \-- We could use mzero to fail, but typeMismatch
198+-- \-- We do not expect a non-'Object' value here.
199+-- \-- We could use 'mzero' to fail, but 'typeMismatch'
160200-- \-- gives a much more informative error message.
161201-- parseJSON invalid = 'typeMismatch' \"Coord\" invalid
162202-- @
163203--
164−-- Note the use of the @OverloadedStrings@ language extension which enables
165−-- 'Text' values to be written as string literals.
166−--
167204-- Instead of manually writing your 'FromJSON' instance, there are two options
168205-- to do it automatically:
169206--
@@ -183,16 +220,16 @@ class ToJSON a where
183220-- @
184221-- {-\# LANGUAGE DeriveGeneric \#-}
185222--
186−-- import GHC.Generics
223+-- import "GHC.Generics"
187224--
188−-- data Coord = Coord { x :: Double, y :: Double } deriving Generic
225+-- data Coord = Coord { x :: Double, y :: Double } deriving 'Generic'
189226--
190227-- instance FromJSON Coord
191228-- @
192229--
193−-- Note that, instead of using @DefaultSignatures@, it's also possible
194−-- to parameterize the generic decoding using 'genericParseJSON' applied
195−-- to your encoding/decoding 'Options':
230+-- If @DefaultSignatures@ doesn't give exactly the results you want,
231+-- you can customize the generic decoding with only a tiny amount of
232+-- effort, using 'genericParseJSON' with your preferred 'Options':
196233--
197234-- @
198235-- instance FromJSON Coord where
@@ -205,9 +242,9 @@ class FromJSON a where
205242 default parseJSON :: (Generic a, GFromJSON (Rep a)) => Value -> Parser a
206243 parseJSON = genericParseJSON defaultOptions
207244
208−-- | A key-value pair for a JSON object.
209−class KeyValue t where
210− (.=) :: ToJSON v => Text -> v -> t
245+-- | A key-value pair for encoding a JSON object.
246+class KeyValue kv where
247+ (.=) :: ToJSON v => Text -> v -> kv
211248 infixr 8 .=
212249
213250-- | Fail parsing due to a type mismatch, with a descriptive message.
@@ -215,7 +252,6 @@ class KeyValue t where
215252-- Example usage:
216253--
217254-- @
218−--
219255-- instance FromJSON Coord where
220256-- parseJSON ('Object' v) = {- type matches, life is good -}
221257-- parseJSON wat = 'typeMismatch' \"Coord\" wat
Data/Aeson/Types/Internal.hs+4−3
@@ -222,7 +222,7 @@ type Failure f r = JSONPath -> String -> f r
222222-- | Success continuation.
223223type Success a f r = a -> f r
224224
225−-- | A continuation-based parser type.
225+-- | A JSON parser.
226226newtype Parser a = Parser {
227227 runParser :: forall f r.
228228 JSONPath
@@ -325,7 +325,7 @@ instance Monoid Series where
325325
326326-- | A newtype wrapper for 'UTCTime' that uses the same non-standard
327327-- serialization format as Microsoft .NET, whose
328−-- <https://msdn.microsoft.com/en-us/library/system.datetime(v=vs.110).aspx @System.DateTime@>
328+-- <https://msdn.microsoft.com/en-us/library/system.datetime(v=vs.110).aspx System.DateTime>
329329-- type is by default serialized to JSON as in the following example:
330330--
331331-- > /Date(1302547608878)/
@@ -390,7 +390,8 @@ parseMaybe :: (a -> Parser b) -> a -> Maybe b
390390parseMaybe m v = runParser (m v) [] (\_ _ -> Nothing) Just
391391{-# INLINE parseMaybe #-}
392392
393−-- | Run a 'Parser' with an 'Either' result type.
393+-- | Run a 'Parser' with an 'Either' result type. If the parse fails,
394+-- the 'Left' payload will contain an error message.
394395parseEither :: (a -> Parser b) -> a -> Either String b
395396parseEither m v = runParser (m v) [] onError Right
396397 where onError path msg = Left (formatError path msg)