on master help sign in
Merge pull request #96 from tibbe/docs

Improve introductionary documentation

commitdedeb4821ca895792e4706e7a3837782e3f5ed9d authorBryan O'Sullivan <bos@serpentine.com> dateFri 7 Dec 2012, 10:09 (14 years ago) parenta59a3e4d671309
1 file changed+122−126
Data/Aeson.hs+122−126
Data/Aeson.hs+122−126
@@ -11,136 +11,20 @@
1111--
1212-- (A note on naming: in Greek mythology, Aeson was the father of Jason.)
1313--
14−-- /DECODING TO AN ADT ('VALUE')/
15−--
16−-- To parse JSON into something useful, everything goes through the
17−-- 'decode' function, which is polymorphic on the 'FromJSON'
18−-- class. For representing arbitrary JSON AST there is a 'Value' type,
19−-- which is an instance of 'FromJSON'. For example:
20−--
21−-- > λ> decode "{\"foo\":123}" :: Maybe Value
22−-- > Just (Object (fromList [("foo",Number 123)]))
23−-- > λ> decode "{\"foo\":[\"abc\",\"def\"]}" :: Maybe Value
24−-- > Just (Object (fromList [("foo",Array (fromList [String "abc",String "def"]))]))
25−--
26−-- To run these examples, you need to enable @OverloadedStrings@ (in
27−-- GHCi you can write @:set -XOverloadedStrings@) so that you can use
28−-- string literals for non-'String' types. We're using (the lazy
29−-- version of) 'Data.ByteString.Lazy.ByteString', which requires at
30−-- least version 0.9.0.4 of the bytestring package to provide the
31−-- 'Data.String.IsString' instance. You probably have something newer
32−-- than this installed.
33−--
34−-- /DECODING TO HASKELL TYPES/
35−--
36−-- Any instance of 'FromJSON' can be specified (but see the PITFALLS section):
37−--
38−-- > λ> decode "[1,2,3]" :: Maybe [Int]
39−-- > Just [1,2,3]
40−--
41−-- Alternatively, there are instances for standard data types, so you
42−-- can use them directly. For example, use the 'Data.Map.Map' type to
43−-- get a map of 'Int's.
44−--
45−-- > λ> :m + Data.Map
46−-- > λ> decode "{\"foo\":1,\"bar\":2}" :: Maybe (Map String Int)
47−-- > Just (fromList [("bar",2),("foo",1)])
48−--
49−-- /DECODING A HETEROGENOUS OBJECT/
50−--
51−-- The above approach with maps of course will not work for
52−-- heterogenous objects, so there are a couple of approaches available
53−-- to you.
54−--
55−-- The 'Object' type contains JSON objects:
56−--
57−-- > λ> decode "{\"name\":\"Dave\",\"age\":2}" :: Maybe Object
58−-- > Just (fromList) [("name",String "Dave"),("age",Number 2)]
59−--
60−-- And you extract values from it with a parser using 'parse',
61−-- 'parseEither' or, in this example, 'parseMaybe':
62−--
63−-- > λ> do result <- decode "{\"name\":\"Dave\",\"age\":2}"
64−-- > flip parseMaybe result $ \obj -> do
65−-- > age <- obj .: "age"
66−-- > name <- obj .: "name"
67−-- > return (name ++ ": " ++ show (age*2))
68−-- >
69−-- > Just "Dave: 4"
70−--
71−-- Considering that any type that implements 'FromJSON' can be used
72−-- here, this is quite a powerful way to parse JSON. See the
73−-- documentation in 'FromJSON' for how to implement this class for
74−-- your own data types.
75−--
76−-- The downside is that you have to write the parser yourself, the
77−-- upside is that you have complete control over the way the JSON is
78−-- parsed.
79−--
80−-- /DECODING CUSTOM DATA TYPES GENERICALLY WITH TYPEABLE/
81−--
82−-- If you don't want such control and would prefer the JSON be parsed
83−-- to your own data types automatically according to some reasonably
84−-- sensible isomorphic implementation, you can use the generic parser
85−-- based on 'Data.Typeable.Typeable' and 'Data.Data.Data'. Switch to
86−-- the 'Data.Aeson.Generic' module, and you can do the following:
87−--
88−-- > λ> decode "[1]" :: Maybe [Int]
89−-- > Just [1]
90−-- > λ> :m + Data.Typeable Data.Data
91−-- > λ> :set -XDeriveDataTypeable
92−-- > λ> data Person = Person { personName :: String, personAge :: Int } deriving (Data,Typeable,Show)
93−-- > λ> encode Person { personName = "Chris", personAge = 123 }
94−-- > "{\"personAge\":123,\"personName\":\"Chris\"}"
95−-- > λ> decode "{\"personAge\":123,\"personName\":\"Chris\"}" :: Maybe Person
96−-- > Just (Person {
97−-- > personName = "Chris", personAge = 123
98−-- > })
99−--
100−-- Be aware that the encoding might not be what you expect:
101−--
102−-- > λ> data Foo = Foo Int Int deriving (Data,Typeable,Show)
103−-- > λ> encode (Foo 1 2)
104−-- > "[1,2]"
105−--
106−-- So it's better to treat the 'Data.Aeson.Generic.decode' and
107−-- 'Data.Aeson.Generic.encode' functions as an isomorphism, but do not
108−-- rely or care about the actual intermediate representation.
109−--
110−-- /PITFALLS/
111−--
112−-- Note that the JSON standard only allows arrays or objects of things
113−-- at the top-level, so calling decode on a simple type will not work:
114−--
115−-- > λ> decode "1" :: Maybe Int
116−-- > Nothing
117−-- > λ> decode "1" :: Maybe String
118−-- > Nothing
119−--
120−-- So stick to objects (e.g. maps in Haskell) or arrays (lists in Haskell):
121−--
122−-- > λ> decode "[1,2,3]" :: Maybe [Int]
123−-- > Just [1,2,3]
124−--
125−-- Likewise, for encoding to JSON you can encode anything that's an
126−-- instance of 'ToJSON', which does include simple types. So beware
127−-- that this aspect of the API is not isomorphic:
128−--
129−-- > λ> encode [1,2,3]
130−-- > "[1,2,3]"
131−-- > λ> decode (encode [1]) :: Maybe [Int]
132−-- > Just [1]
133−-- > λ> encode 1
134−-- > "1"
135−-- > λ> decode (encode (1 :: Int)) :: Maybe Int
136−-- > Nothing
137−--
138−-- Alternatively see 'Data.Aeson.Parser.value' to parse non-toplevel
139−-- JSON values.
14014
14115module Data.Aeson
14216 (
17+ -- * Usage example
18+ -- $usage-example
19+
20+ -- * Working directly with the JSON AST
21+ -- $json-ast
22+
23+ -- * Pitfalls
24+ -- $pitfalls
25+
14326 -- * Encoding and decoding
27+ -- $encoding-and-decoding
14428 decode
14529 , decode'
14630 , eitherDecode
@@ -208,3 +92,115 @@ eitherDecode = eitherDecodeWith json fromJSON
20892eitherDecode' :: (FromJSON a) => L.ByteString -> Either String a
20993eitherDecode' = eitherDecodeWith json' fromJSON
21094{-# INLINE eitherDecode' #-}
95+
96+-- $usage-example
97+--
98+-- The most common way to use the library is to define a data type,
99+-- corresponding to some JSON data you want to work with, and then
100+-- write either a 'FromJSON' instance, a to 'ToJSON' instance, or both
101+-- for that type. For example, given this JSON data:
102+--
103+-- > { "name": "Joe", "age": 12 }
104+--
105+-- we create a matching data type:
106+--
107+-- > data Person = Person
108+-- > { name :: Text
109+-- > , age :: Int
110+-- > } deriving Show
111+--
112+-- To decode data, we need to define a 'FromJSON' instance:
113+--
114+-- > {-# LANGUAGE OverloadedStrings #-}
115+-- >
116+-- > instance FromJSON Coord where
117+-- > parseJSON (Object v) = Person <$>
118+-- > v .: "name" <*>
119+-- > v .: "age"
120+-- > -- A non-Object value is of the wrong type, so fail.
121+-- > parseJSON _ = mzero
122+--
123+-- We can now parse the JSON data like so:
124+--
125+-- > >>> decode "{\"name\":\"Joe\",\"age\":12}" :: Maybe Person
126+-- > Just (Person {name = "Joe", age = 12})
127+--
128+-- The explicit type signature can often be omitted as the compiler
129+-- can deduce the type using type inference.
130+--
131+-- To encode data, we need to define a 'ToJSON' instance:
132+--
133+-- > instance ToJSON Person where
134+-- > toJSON (Person name age) = object ["name" .= name, "age" .= age]
135+--
136+-- We can now encode a value like so:
137+--
138+-- > >>> encode (Person {name = "Joe", age = 12})
139+-- > "{\"name\":\"Joe\",\"age\":12}"
140+--
141+-- There are predefined 'FromJSON' and 'ToJSON' instances for many
142+-- types. Here's an example using lists and 'Int's:
143+--
144+-- > >>> decode "[1,2,3]" :: Maybe [Int]
145+-- > Just [1,2,3]
146+--
147+-- And here's an example using the 'Data.Map.Map' type to get a map of
148+-- 'Int's.
149+--
150+-- > >>> decode "{\"foo\":1,\"bar\":2}" :: Maybe (Map String Int)
151+-- > Just (fromList [("bar",2),("foo",1)])
152+--
153+-- See the documentation of 'FromJSON' and 'ToJSON' for some examples
154+-- how you can automatically dervice instances in some circumstances.
155+
156+
157+-- $json-ast
158+--
159+-- Sometimes you want to work with JSON data directly, without first
160+-- converting it to a custom data type. This can be useful if you want
161+-- to e.g. convert JSON data to YAML data, without knowing what the
162+-- contents of the original JSON data was. The 'Value' type, which is
163+-- an instance of 'FromJSON', is used to represent an arbitrary JSON
164+-- AST. Example usage:
165+--
166+-- > >>> decode "{\"foo\": 123}" :: Maybe Value
167+-- > Just (Object (fromList [("foo",Number 123)]))
168+-- > >>> decode "{\"foo\": [\"abc\",\"def\"]}" :: Maybe Value
169+-- > Just (Object (fromList [("foo",Array (fromList [String "abc",String "def"]))]))
170+--
171+-- Once you have a 'Value' you can write recursive functions to
172+-- traverse it and make arbitrary transformations.
173+
174+-- $pitfalls
175+--
176+-- Note that the JSON standard only allows arrays or objects of things
177+-- at the top-level, so calling decode on a simple type will not work:
178+--
179+-- > >>> decode "1" :: Maybe Int
180+-- > Nothing
181+-- > >>> decode "1" :: Maybe String
182+-- > Nothing
183+--
184+-- Likewise, for encoding to JSON you can encode anything that's an
185+-- instance of 'ToJSON', which does include simple types. So beware
186+-- that this aspect of the API is not isomorphic:
187+--
188+-- > >>> encode [1,2,3]
189+-- > "[1,2,3]"
190+-- > >>> decode (encode [1]) :: Maybe [Int]
191+-- > Just [1]
192+-- > >>> encode 1
193+-- > "1"
194+-- > >>> decode (encode (1 :: Int)) :: Maybe Int
195+-- > Nothing
196+--
197+-- Alternatively see 'Data.Aeson.Parser.value' to parse non-toplevel
198+-- JSON values.
199+
200+-- $encoding-and-decoding
201+--
202+-- Encoding and decoding is a two step process. To encode a value, it
203+-- is first converted to a generic representation, using 'ToJSON'. The
204+-- generic representation is then encoded as JSON data. To decode a
205+-- value the process is reversed and 'FromJSON' is used instead. Both
206+-- these steps are combined in the 'encode' and 'decode' functions.