Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Add elaborate guide to Data.Aeson's Haddock documentation #91

Merged
merged 1 commit into from

2 participants

@chrisdone

After some discussion and here I've written some example-ridden documentation for the library. It's not complete, but it covers the basics that I'm aware of. I hope this can make aeson more approachable to newbies. Cheers.

@bos bos merged commit 4a3d1a1 into from
@bos
Owner

Nice work, Chris!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
Showing with 127 additions and 0 deletions.
  1. +127 −0 Data/Aeson.hs
View
127 Data/Aeson.hs
@@ -10,6 +10,133 @@
-- Types and functions for working efficiently with JSON data.
--
-- (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
(
Something went wrong with that request. Please try again.