Source

aeson / Data / Aeson.hs

-- |
-- Module:      Data.Aeson
-- Copyright:   (c) 2011 Bryan O'Sullivan
--              (c) 2011 MailRank, Inc.
-- License:     Apache
-- Maintainer:  Bryan O'Sullivan <bos@serpentine.com>
-- Stability:   experimental
-- Portability: portable
--
-- 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
    (
    -- * Encoding and decoding
      decode
    , decode'
    , encode
    -- * Core JSON types
    , Value(..)
    , Array
    , Object
    -- * Convenience types
    , DotNetTime(..)
    -- * Type conversion
    , FromJSON(..)
    , Result(..)
    , fromJSON
    , ToJSON(..)
    -- * Constructors and accessors
    , (.=)
    , (.:)
    , (.:?)
    , (.!=)
    , object
    -- * Parsing
    , json
    , json'
    ) where

import Data.Aeson.Encode (encode)
import Data.Aeson.Parser.Internal (decodeWith, json, json')
import Data.Aeson.Types
import qualified Data.ByteString.Lazy as L

-- | Efficiently deserialize a JSON value from a lazy 'L.ByteString'.
-- If this fails due to incomplete or invalid input, 'Nothing' is
-- returned.
--
-- This function parses immediately, but defers conversion.  See
-- 'json' for details.
decode :: (FromJSON a) => L.ByteString -> Maybe a
decode = decodeWith json fromJSON
{-# INLINE decode #-}

-- | Efficiently deserialize a JSON value from a lazy 'L.ByteString'.
-- If this fails due to incomplete or invalid input, 'Nothing' is
-- returned.
--
-- This function parses and performs conversion immediately.  See
-- 'json'' for details.
decode' :: (FromJSON a) => L.ByteString -> Maybe a
decode' = decodeWith json' fromJSON
{-# INLINE decode' #-}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.