aeson-2.2.0.0: src/Data/Aeson.hs
{-# LANGUAGE ScopedTypeVariables #-}
-- |
-- Module: Data.Aeson
-- Copyright: (c) 2011-2016 Bryan O'Sullivan
-- (c) 2011 MailRank, Inc.
-- License: BSD3
-- 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.)
module Data.Aeson
(
-- * How to use this library
-- $use
-- ** Writing instances by hand
-- $manual
-- ** Working with the AST
-- $ast
-- ** Decoding to a Haskell value
-- $haskell
-- ** Decoding a mixed-type object
-- $mixed
-- * Encoding and decoding
-- $encoding_and_decoding
-- ** Direct encoding
-- $encoding
-- * Remarks on specific encodings
-- ** Time
-- $time
-- * Main encoding and decoding functions
decode
, decode'
, eitherDecode
, eitherDecode'
, encode
, encodeFile
-- ** Variants for strict bytestrings
, decodeStrict
, decodeFileStrict
, decodeStrict'
, decodeFileStrict'
, eitherDecodeStrict
, eitherDecodeFileStrict
, eitherDecodeStrict'
, eitherDecodeFileStrict'
-- ** Exception throwing variants
, AesonException (..)
, throwDecode
, throwDecodeStrict
, throwDecode'
, throwDecodeStrict'
-- * Core JSON types
, Value(..)
, Encoding
, fromEncoding
, Array
, Object
, Key
-- * Convenience types
, DotNetTime(..)
-- * Type conversion
, FromJSON(..)
, Result(..)
, fromJSON
, ToJSON(..)
, KeyValue(..)
, KeyValueOmit(..)
, (<?>)
, JSONPath
-- ** Keys for maps
, ToJSONKey(..)
, ToJSONKeyFunction(..)
, FromJSONKey(..)
, FromJSONKeyFunction(..)
-- *** Generic keys
, GToJSONKey()
, genericToJSONKey
, GFromJSONKey()
, genericFromJSONKey
-- ** Liftings to unary and binary type constructors
, FromJSON1(..)
, parseJSON1
, omittedField1
, FromJSON2(..)
, parseJSON2
, omittedField2
, ToJSON1(..)
, toJSON1
, toEncoding1
, omitField1
, ToJSON2(..)
, toJSON2
, toEncoding2
, omitField2
-- ** Generic JSON classes and options
, GFromJSON
, FromArgs
, GToJSON
, GToEncoding
, GToJSON'
, ToArgs
, Zero
, One
, genericToJSON
, genericLiftToJSON
, genericToEncoding
, genericLiftToEncoding
, genericParseJSON
, genericLiftParseJSON
-- ** Generic and TH encoding configuration
, Options
, defaultOptions
-- *** Options fields
-- $optionsFields
, fieldLabelModifier
, constructorTagModifier
, allNullaryToStringTag
, omitNothingFields
, allowOmittedFields
, sumEncoding
, unwrapUnaryRecords
, tagSingleConstructors
, rejectUnknownFields
-- *** Options utilities
, SumEncoding(..)
, camelTo2
, defaultTaggedObject
-- ** Options for object keys
, JSONKeyOptions
, keyModifier
, defaultJSONKeyOptions
-- * Inspecting @'Value's@
, withObject
, withText
, withArray
, withScientific
, withBool
, withEmbeddedJSON
-- * Constructors and accessors
, Series
, pairs
, foldable
, (.:)
, (.:?)
, (.:!)
, (.!=)
, (.:?=)
, (.:!=)
, object
-- * Parsing
, parseIndexedJSON
) where
import Control.Monad.Catch (MonadThrow (..))
import Data.Aeson.Types.FromJSON (parseIndexedJSON)
import Data.Aeson.Encoding (encodingToLazyByteString)
import Data.Aeson.Types
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as L
import Data.Aeson.Decoding (decode, eitherDecode, throwDecode, decodeStrict, eitherDecodeStrict, throwDecodeStrict)
-- $setup
-- >>> :set -XOverloadedStrings
-- | Efficiently serialize a JSON value as a lazy 'L.ByteString'.
--
-- This is implemented in terms of the 'ToJSON' class's 'toEncoding' method.
encode :: (ToJSON a) => a -> L.ByteString
encode = encodingToLazyByteString . toEncoding
-- | Efficiently serialize a JSON value as a lazy 'L.ByteString' and write it to a file.
encodeFile :: (ToJSON a) => FilePath -> a -> IO ()
encodeFile fp = L.writeFile fp . encode
-- | Efficiently deserialize a JSON value from a file.
-- If this fails due to incomplete or invalid input, 'Nothing' is
-- returned.
--
-- The input file's content must consist solely of a JSON document,
-- with no trailing data except for whitespace.
--
-- This function parses immediately, but defers conversion. See
-- 'json' for details.
decodeFileStrict :: (FromJSON a) => FilePath -> IO (Maybe a)
decodeFileStrict = fmap decodeStrict . B.readFile
-- | Efficiently deserialize a JSON value from a lazy 'L.ByteString'.
-- If this fails due to incomplete or invalid input, 'Nothing' is
-- returned.
--
-- Since @2.2.0.0@ an alias for 'decode'.
--
decode' :: (FromJSON a) => L.ByteString -> Maybe a
decode' = decode
{-# INLINE decode' #-}
-- | Efficiently deserialize a JSON value from a strict 'B.ByteString'.
-- If this fails due to incomplete or invalid input, 'Nothing' is
-- returned.
--
-- Since @2.2.0.0@ an alias for 'decodeStrict'.
--
decodeStrict' :: (FromJSON a) => B.ByteString -> Maybe a
decodeStrict' = decodeStrict
{-# INLINE decodeStrict' #-}
-- | Efficiently deserialize a JSON value from a file.
-- If this fails due to incomplete or invalid input, 'Nothing' is
-- returned.
--
-- Since @2.2.0.0@ an alias for 'decodeFileStrict'.
--
decodeFileStrict' :: (FromJSON a) => FilePath -> IO (Maybe a)
decodeFileStrict' = decodeFileStrict
-- | Like 'decodeFileStrict' but returns an error message when decoding fails.
eitherDecodeFileStrict :: (FromJSON a) => FilePath -> IO (Either String a)
eitherDecodeFileStrict =
fmap eitherDecodeStrict . B.readFile
{-# INLINE eitherDecodeFileStrict #-}
-- | Like 'decode'' but returns an error message when decoding fails.
--
-- Since @2.2.0.0@ an alias for 'eitherDecode'.
eitherDecode' :: (FromJSON a) => L.ByteString -> Either String a
eitherDecode' = eitherDecode
{-# INLINE eitherDecode' #-}
-- | Like 'decodeStrict'' but returns an error message when decoding fails.
--
-- Since @2.2.0.0@ an alias for 'eitherDecodeStrict'.
eitherDecodeStrict' :: (FromJSON a) => B.ByteString -> Either String a
eitherDecodeStrict' = eitherDecodeStrict
{-# INLINE eitherDecodeStrict' #-}
-- | Like 'decodeFileStrict'' but returns an error message when decoding fails.
--
-- Since @2.2.0.0@ an alias for 'eitherDecodeFileStrict''.
eitherDecodeFileStrict' :: (FromJSON a) => FilePath -> IO (Either String a)
eitherDecodeFileStrict' = eitherDecodeFileStrict
{-# INLINE eitherDecodeFileStrict' #-}
-- | Like 'decode'' but throws an 'AesonException' when decoding fails.
--
-- Since @2.2.0.0@ an alias for 'throwDecode'.
--
-- @since 2.1.2.0
--
throwDecode' :: forall a m. (FromJSON a, MonadThrow m) => L.ByteString -> m a
throwDecode' = throwDecode
{-# INLINE throwDecode' #-}
-- | Like 'decodeStrict'' but throws an 'AesonException' when decoding fails.
--
-- Since @2.2.0.0@ an alias for 'throwDecodeStrict'.
--
-- @since 2.1.2.0
--
throwDecodeStrict' :: forall a m. (FromJSON a, MonadThrow m) => B.ByteString -> m a
throwDecodeStrict' = throwDecodeStrict
{-# INLINE throwDecodeStrict' #-}
-- $use
--
-- This section contains basic information on the different ways to
-- work with data using this library. These range from simple but
-- inflexible, to complex but flexible.
--
-- 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:
--
-- > {-# LANGUAGE DeriveGeneric #-}
-- >
-- > import GHC.Generics
-- >
-- > data Person = Person {
-- > name :: Text
-- > , age :: Int
-- > } deriving (Generic, Show)
--
-- The @LANGUAGE@ pragma and 'Generic' instance let us write empty
-- 'FromJSON' and 'ToJSON' instances for which the compiler will
-- generate sensible default implementations.
--
-- @
-- instance 'ToJSON' Person where
-- \-- No need to provide a 'toJSON' implementation.
--
-- \-- For efficiency, we write a simple 'toEncoding' implementation, as
-- \-- the default version uses 'toJSON'.
-- 'toEncoding' = 'genericToEncoding' 'defaultOptions'
--
-- instance 'FromJSON' Person
-- \-- No need to provide a 'parseJSON' implementation.
-- @
--
-- We can now encode a value like so:
--
-- > >>> encode (Person {name = "Joe", age = 12})
-- > "{\"name\":\"Joe\",\"age\":12}"
-- $manual
--
-- When necessary, we can write 'ToJSON' and 'FromJSON' instances by
-- hand. This is valuable when the JSON-on-the-wire and Haskell data
-- are different or otherwise need some more carefully managed
-- translation. Let's revisit our JSON data:
--
-- > { "name": "Joe", "age": 12 }
--
-- We once again create a matching data type, without bothering to add
-- a 'Generic' instance this time:
--
-- > data Person = Person {
-- > name :: Text
-- > , age :: Int
-- > } deriving Show
--
-- To decode data, we need to define a 'FromJSON' instance:
--
-- > {-# LANGUAGE OverloadedStrings #-}
-- >
-- > instance FromJSON Person where
-- > parseJSON = withObject "Person" $ \v -> Person
-- > <$> v .: "name"
-- > <*> v .: "age"
--
-- We can now parse the JSON data like so:
--
-- > >>> decode "{\"name\":\"Joe\",\"age\":12}" :: Maybe Person
-- > Just (Person {name = "Joe", age = 12})
--
-- To encode data, we need to define a 'ToJSON' instance. Let's begin
-- with an instance written entirely by hand.
--
-- @
-- instance ToJSON Person where
-- \-- this generates a 'Value'
-- 'toJSON' (Person name age) =
-- 'object' [\"name\" '.=' name, \"age\" '.=' age]
--
-- \-- this encodes directly to a bytestring Builder
-- 'toEncoding' (Person name age) =
-- 'pairs' (\"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)])
-- While the notes below focus on decoding, you can apply almost the
-- same techniques to /encoding/ data. (The main difference is that
-- encoding always succeeds, but decoding has to handle the
-- possibility of failure, where an input doesn't match our
-- expectations.)
--
-- See the documentation of 'FromJSON' and 'ToJSON' for some examples
-- of how you can automatically derive instances in many common
-- circumstances.
-- $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 (abstract syntax tree). 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 functions to traverse it and
-- make arbitrary transformations.
-- $haskell
--
-- We can decode to any instance of 'FromJSON':
--
-- > λ> 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.
--
-- > λ> import Data.Map
-- > λ> decode "{\"foo\":1,\"bar\":2}" :: Maybe (Map String Int)
-- > Just (fromList [("bar",2),("foo",1)])
-- $mixed
--
-- The above approach with maps of course will not work for mixed-type
-- objects that don't follow a strict schema, but there are a couple
-- of approaches available for these.
--
-- The 'Object' type contains JSON objects:
--
-- > λ> decode "{\"name\":\"Dave\",\"age\":2}" :: Maybe Object
-- > Just (fromList [("name",String "Dave"),("age",Number 2)])
--
-- You can 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.
-- $encoding_and_decoding
--
-- Decoding is a two-step process.
--
-- * When decoding a value, the process is reversed: the bytes are
-- converted to a 'Value', then the 'FromJSON' class is used to
-- convert to the desired type.
--
-- There are two ways to encode a value.
--
-- * Convert to a 'Value' using 'toJSON', then possibly further
-- encode. This was the only method available in aeson 0.9 and
-- earlier.
--
-- * Directly encode (to what will become a 'L.ByteString') using
-- 'toEncoding'. This is much more efficient (about 3x faster, and
-- less memory intensive besides), but is only available in aeson
-- 0.10 and newer.
--
-- For convenience, the 'encode' and 'decode' functions combine both
-- steps.
-- $encoding
--
-- In older versions of this library, encoding a Haskell value
-- involved converting to an intermediate 'Value', then encoding that.
--
-- A \"direct\" encoder converts straight from a source Haskell value
-- to a 'BL.ByteString' without constructing an intermediate 'Value'.
-- This approach is faster than 'toJSON', and allocates less memory.
-- The 'toEncoding' method makes it possible to implement direct
-- encoding with low memory overhead.
--
-- To complicate matters, the default implementation of 'toEncoding'
-- uses 'toJSON'. Why? The 'toEncoding' method was added to this
-- library much more recently than 'toJSON'. Using 'toJSON' ensures
-- that packages written against older versions of this library will
-- compile and produce correct output, but they will not see any
-- speedup from direct encoding.
--
-- To write a minimal implementation of direct encoding, your type
-- must implement GHC's 'Generic' class, and your code should look
-- like this:
--
-- @
-- 'toEncoding' = 'genericToEncoding' 'defaultOptions'
-- @
--
-- What if you have more elaborate encoding needs? For example,
-- perhaps you need to change the names of object keys, omit parts of
-- a value.
--
-- To encode to a JSON \"object\", use the 'pairs' function.
--
-- @
-- 'toEncoding' (Person name age) =
-- 'pairs' (\"name\" '.=' 'name' '<>' \"age\" '.=' age)
-- @
--
-- Any container type that implements 'Foldable' can be encoded to a
-- JSON \"array\" using 'foldable'.
--
-- > > import Data.Sequence as Seq
-- > > encode (Seq.fromList [1,2,3])
-- > "[1,2,3]"
-- $time
--
-- This module contains instances of 'ToJSON' and 'FromJSON' for types from
-- the <https://hackage.haskell.org/package/time time> library.
--
-- Those instances encode time as JSON strings in
-- <https://en.wikipedia.org/wiki/ISO_8601 ISO 8601> formats, with the
-- following general form for 'Data.Time.Clock.UTCTime' and
-- 'Data.Time.LocalTime.ZonedTime', while other time types use subsets of those
-- fields:
--
-- > [+,-]YYYY-MM-DDThh:mm[:ss[.sss]]Z
--
-- where
--
-- - @[+,-]@ is an optional sign, @+@ or @-@.
-- - @YYYY@ is the year, which must have at least 4 digits to prevent Y2K problems.
-- Years from @0000@ to @0999@ must thus be zero-padded.
-- - @MM@ is a two-digit month.
-- - @DD@ is a two-digit day.
-- - @T@ is a literal @\'T\'@ character separating the date and the time of
-- day. It may be a space instead.
-- - @hh@ is a two-digit hour.
-- - @mm@ is a two-digit minute.
-- - @ss@ is a two-digit second.
-- - @sss@ is a decimal fraction of a second; it may have any nonzero number of digits.
-- - @Z@ is a time zone; it may be preceded by an optional space.
--
-- For more information, see <https://en.wikipedia.org/wiki/ISO_8601 ISO 8601>
-- <https://hackage.haskell.org/package/time time>,
-- and <https://hackage.haskell.org/package/attoparsec-iso8601 attoparsec-iso8601>
-- (where the relevant parsers are defined).