on master help sign in
Add elaborate guide to Data.Aeson's Haddock documentation.
commitfade9f4dbc23088b8953f0bac3f8dec3b42b59cf authorChris Done <chrisdone@gmail.com> dateFri 16 Nov 2012, 19:10 (14 years ago) parent9e8c3c9
1 file changed+127
Data/Aeson.hs+127
Data/Aeson.hs+127
@@ -10,6 +10,133 @@
1010-- Types and functions for working efficiently with JSON data.
1111--
1212-- (A note on naming: in Greek mythology, Aeson was the father of Jason.)
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.
13140
14141module Data.Aeson
15142 (