Source

aeson / Data / Aeson.hs

The default branch has multiple heads

Diff from to

File Data/Aeson.hs

 --
 -- (A note on naming: in Greek mythology, Aeson was the father of Jason.)
 --
--- /DECODING TO AN ADT ('VALUE')/
---
--- To parse JSON into something useful, everything goes through the
--- 'decode' function, which is polymorphic on the 'FromJSON'
--- class. For representing arbitrary JSON AST there is a 'Value' type,
--- which is an instance of 'FromJSON'. For example:
---
--- > λ> decode "{\"foo\":123}" :: Maybe Value
--- > Just (Object (fromList [("foo",Number 123)]))
--- > λ> decode "{\"foo\":[\"abc\",\"def\"]}" :: Maybe Value
--- > Just (Object (fromList [("foo",Array (fromList [String "abc",String "def"]))]))
---
--- To run these examples, you need to enable @OverloadedStrings@ (in
--- GHCi you can write @:set -XOverloadedStrings@) so that you can use
--- string literals for non-'String' types. We're using (the lazy
--- version of) 'Data.ByteString.Lazy.ByteString', which requires at
--- least version 0.9.0.4 of the bytestring package to provide the
--- 'Data.String.IsString' instance. You probably have something newer
--- than this installed.
---
--- /DECODING TO HASKELL TYPES/
---
--- Any instance of 'FromJSON' can be specified (but see the PITFALLS section):
---
--- > λ> decode "[1,2,3]" :: Maybe [Int]
--- > Just [1,2,3]
---
--- Alternatively, there are instances for standard data types, so you
--- can use them directly. For example, use the 'Data.Map.Map' type to
--- get a map of 'Int's.
---
--- > λ> :m + Data.Map
--- > λ> decode "{\"foo\":1,\"bar\":2}" :: Maybe (Map String Int)
--- > Just (fromList [("bar",2),("foo",1)])
---
--- /DECODING A HETEROGENOUS OBJECT/
---
--- The above approach with maps of course will not work for
--- heterogenous objects, so there are a couple of approaches available
--- to you.
---
--- The 'Object' type contains JSON objects:
---
--- > λ> decode "{\"name\":\"Dave\",\"age\":2}" :: Maybe Object
--- > Just (fromList) [("name",String "Dave"),("age",Number 2)]
---
--- And you extract values from it with a parser using 'parse',
--- 'parseEither' or, in this example, 'parseMaybe':
---
--- > λ> do result <- decode "{\"name\":\"Dave\",\"age\":2}"
--- >       flip parseMaybe result $ \obj -> do
--- >         age <- obj .: "age"
--- >         name <- obj .: "name"
--- >         return (name ++ ": " ++ show (age*2))
--- >
--- > Just "Dave: 4"
---
--- Considering that any type that implements 'FromJSON' can be used
--- here, this is quite a powerful way to parse JSON. See the
--- documentation in 'FromJSON' for how to implement this class for
--- your own data types.
---
--- The downside is that you have to write the parser yourself, the
--- upside is that you have complete control over the way the JSON is
--- parsed.
---
--- /DECODING CUSTOM DATA TYPES GENERICALLY WITH TYPEABLE/
---
--- If you don't want such control and would prefer the JSON be parsed
--- to your own data types automatically according to some reasonably
--- sensible isomorphic implementation, you can use the generic parser
--- based on 'Data.Typeable.Typeable' and 'Data.Data.Data'. Switch to
--- the 'Data.Aeson.Generic' module, and you can do the following:
---
--- > λ> decode "[1]" :: Maybe [Int]
--- > Just [1]
--- > λ> :m + Data.Typeable Data.Data
--- > λ> :set -XDeriveDataTypeable
--- > λ> data Person = Person { personName :: String, personAge :: Int } deriving (Data,Typeable,Show)
--- > λ> encode Person { personName = "Chris", personAge = 123 }
--- > "{\"personAge\":123,\"personName\":\"Chris\"}"
--- > λ> decode "{\"personAge\":123,\"personName\":\"Chris\"}" :: Maybe Person
--- > Just (Person {
--- > personName = "Chris", personAge = 123
--- > })
---
--- Be aware that the encoding might not be what you expect:
---
--- > λ> data Foo = Foo Int Int deriving (Data,Typeable,Show)
--- > λ> encode (Foo 1 2)
--- > "[1,2]"
---
--- So it's better to treat the 'Data.Aeson.Generic.decode' and
--- 'Data.Aeson.Generic.encode' functions as an isomorphism, but do not
--- rely or care about the actual intermediate representation.
---
--- /PITFALLS/
---
--- Note that the JSON standard only allows arrays or objects of things
--- at the top-level, so calling decode on a simple type will not work:
---
--- > λ> decode "1" :: Maybe Int
--- > Nothing
--- > λ> decode "1" :: Maybe String
--- > Nothing
---
--- So stick to objects (e.g. maps in Haskell) or arrays (lists in Haskell):
---
--- > λ> decode "[1,2,3]" :: Maybe [Int]
--- > Just [1,2,3]
---
--- Likewise, for encoding to JSON you can encode anything that's an
--- instance of 'ToJSON', which does include simple types. So beware
--- that this aspect of the API is not isomorphic:
---
--- > λ> encode [1,2,3]
--- > "[1,2,3]"
--- > λ> decode (encode [1]) :: Maybe [Int]
--- > Just [1]
--- > λ> encode 1
--- > "1"
--- > λ> decode (encode (1 :: Int)) :: Maybe Int
--- > Nothing
---
--- Alternatively see 'Data.Aeson.Parser.value' to parse non-toplevel
--- JSON values.
 
 module Data.Aeson
     (
+    -- * Usage example
+    -- $usage-example
+
+    -- * Working directly with the JSON AST
+    -- $json-ast
+
+    -- * Pitfalls
+    -- $pitfalls
+
     -- * Encoding and decoding
+    -- $encoding-and-decoding
       decode
     , decode'
     , eitherDecode
 eitherDecode' :: (FromJSON a) => L.ByteString -> Either String a
 eitherDecode' = eitherDecodeWith json' fromJSON
 {-# INLINE eitherDecode' #-}
+
+-- $usage-example
+--
+-- The most common way to use the library is to define a data type,
+-- corresponding to some JSON data you want to work with, and then
+-- write either a 'FromJSON' instance, a to 'ToJSON' instance, or both
+-- for that type. For example, given this JSON data:
+--
+-- > { "name": "Joe", "age": 12 }
+--
+-- we create a matching data type:
+--
+-- > data Person = Person
+-- >     { name :: Text
+-- >     , age  :: Int
+-- >     } deriving Show
+--
+-- To decode data, we need to define a 'FromJSON' instance:
+--
+-- > {-# LANGUAGE OverloadedStrings #-}
+-- >
+-- > instance FromJSON Coord where
+-- >     parseJSON (Object v) = Person <$>
+-- >                            v .: "name" <*>
+-- >                            v .: "age"
+-- >     -- A non-Object value is of the wrong type, so fail.
+-- >     parseJSON _          = mzero
+--
+-- We can now parse the JSON data like so:
+--
+-- > >>> decode "{\"name\":\"Joe\",\"age\":12}" :: Maybe Person
+-- > Just (Person {name = "Joe", age = 12})
+--
+-- The explicit type signature can often be omitted as the compiler
+-- can deduce the type using type inference.
+--
+-- To encode data, we need to define a 'ToJSON' instance:
+--
+-- > instance ToJSON Person where
+-- >     toJSON (Person name age) = object ["name" .= name, "age" .= age]
+--
+-- We can now encode a value like so:
+--
+-- > >>> encode (Person {name = "Joe", age = 12})
+-- > "{\"name\":\"Joe\",\"age\":12}"
+--
+-- There are predefined 'FromJSON' and 'ToJSON' instances for many
+-- types. Here's an example using lists and 'Int's:
+--
+-- > >>> decode "[1,2,3]" :: Maybe [Int]
+-- > Just [1,2,3]
+--
+-- And here's an example using the 'Data.Map.Map' type to get a map of
+-- 'Int's.
+--
+-- > >>> decode "{\"foo\":1,\"bar\":2}" :: Maybe (Map String Int)
+-- > Just (fromList [("bar",2),("foo",1)])
+--
+-- See the documentation of 'FromJSON' and 'ToJSON' for some examples
+-- how you can automatically dervice instances in some circumstances.
+
+
+-- $json-ast
+--
+-- Sometimes you want to work with JSON data directly, without first
+-- converting it to a custom data type. This can be useful if you want
+-- to e.g. convert JSON data to YAML data, without knowing what the
+-- contents of the original JSON data was. The 'Value' type, which is
+-- an instance of 'FromJSON', is used to represent an arbitrary JSON
+-- AST. Example usage:
+--
+-- > >>> decode "{\"foo\": 123}" :: Maybe Value
+-- > Just (Object (fromList [("foo",Number 123)]))
+-- > >>> decode "{\"foo\": [\"abc\",\"def\"]}" :: Maybe Value
+-- > Just (Object (fromList [("foo",Array (fromList [String "abc",String "def"]))]))
+--
+-- Once you have a 'Value' you can write recursive functions to
+-- traverse it and make arbitrary transformations.
+
+-- $pitfalls
+--
+-- Note that the JSON standard only allows arrays or objects of things
+-- at the top-level, so calling decode on a simple type will not work:
+--
+-- > >>> decode "1" :: Maybe Int
+-- > Nothing
+-- > >>> decode "1" :: Maybe String
+-- > Nothing
+--
+-- Likewise, for encoding to JSON you can encode anything that's an
+-- instance of 'ToJSON', which does include simple types. So beware
+-- that this aspect of the API is not isomorphic:
+--
+-- > >>> encode [1,2,3]
+-- > "[1,2,3]"
+-- > >>> decode (encode [1]) :: Maybe [Int]
+-- > Just [1]
+-- > >>> encode 1
+-- > "1"
+-- > >>> decode (encode (1 :: Int)) :: Maybe Int
+-- > Nothing
+--
+-- Alternatively see 'Data.Aeson.Parser.value' to parse non-toplevel
+-- JSON values.
+
+-- $encoding-and-decoding
+--
+-- Encoding and decoding is a two step process. To encode a value, it
+-- is first converted to a generic representation, using 'ToJSON'. The
+-- generic representation is then encoded as JSON data. To decode a
+-- value the process is reversed and 'FromJSON' is used instead. Both
+-- these steps are combined in the 'encode' and 'decode' functions.