Skip to content
This repository

Improve introductionary documentation #96

Merged
merged 1 commit into from over 1 year ago

3 participants

Johan Tibell Adrian Patino Bryan O'Sullivan
Johan Tibell

De-emphasize working directly with the Value type as this is not the
common (or recommended) way to use the library, but still include
documentation on how to use it when it's called for.

Since the documentation on using generics is documented in the FromJSON
and ToJSON classes, refer to that documentation instead of only
documenting one method (which is not the preferred method anymore with
default signatures I believe).

Johan Tibell Improve introductionary documentation
De-emphasize working directly with the Value type as this is not the
common (or recommended) way to use the library, but still include
documentation on how to use it when it's called for.

Since the documentation on using generics is documented in the FromJSON
and ToJSON classes, refer to that documentation instead of only
documenting one method (which is not the preferred method anymore with
default signatures I believe).
ed16167
Bryan O'Sullivan bos merged commit 83a2433 into from December 07, 2012
Bryan O'Sullivan bos closed this December 07, 2012
Adrian Patino

Coord should be Person.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 1 unique commit by 1 author.

Nov 30, 2012
Johan Tibell Improve introductionary documentation
De-emphasize working directly with the Value type as this is not the
common (or recommended) way to use the library, but still include
documentation on how to use it when it's called for.

Since the documentation on using generics is documented in the FromJSON
and ToJSON classes, refer to that documentation instead of only
documenting one method (which is not the preferred method anymore with
default signatures I believe).
ed16167
This page is out of date. Refresh to see the latest.

Showing 1 changed file with 122 additions and 126 deletions. Show diff stats Hide diff stats

  1. 248  Data/Aeson.hs
248  Data/Aeson.hs
@@ -11,136 +11,20 @@
11 11
 --
12 12
 -- (A note on naming: in Greek mythology, Aeson was the father of Jason.)
13 13
 --
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.
140 14
 
141 15
 module Data.Aeson
142 16
     (
  17
+    -- * Usage example
  18
+    -- $usage-example
  19
+
  20
+    -- * Working directly with the JSON AST
  21
+    -- $json-ast
  22
+
  23
+    -- * Pitfalls
  24
+    -- $pitfalls
  25
+
143 26
     -- * Encoding and decoding
  27
+    -- $encoding-and-decoding
144 28
       decode
145 29
     , decode'
146 30
     , eitherDecode
@@ -208,3 +92,115 @@ eitherDecode = eitherDecodeWith json fromJSON
208 92
 eitherDecode' :: (FromJSON a) => L.ByteString -> Either String a
209 93
 eitherDecode' = eitherDecodeWith json' fromJSON
210 94
 {-# 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.
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.