aeson-0.6.1.0: Fast JSON parsing and encoding

Portability | portable
Stability | experimental
Maintainer | Bryan O'Sullivan <bos@serpentine.com>
Safe Haskell | None

Data.Aeson

Contents

- How to use this library
  - Working with the AST
  - Decoding to a Haskell value
  - Decoding a mixed-type object
  - Automatically decoding data types
  - Pitfalls
- Encoding and decoding
- Core JSON types
- Convenience types
- Type conversion
- Inspecting ``Value`s`
- Constructors and accessors
- Parsing

Description

Types and functions for working efficiently with JSON data.

(A note on naming: in Greek mythology, Aeson was the father of Jason.)

Synopsis

- decode :: FromJSON a => ByteString -> Maybe a
- decode' :: FromJSON a => ByteString -> Maybe a
- eitherDecode :: FromJSON a => ByteString -> Either String a
- eitherDecode' :: FromJSON a => ByteString -> Either String a
- encode :: ToJSON a => a -> ByteString
- data Value
  - = Object !Object
  - | Array !Array
  - | String !Text
  - | Number !Number
  - | Bool !Bool
  - | Null
- type Array = Vector Value
- type Object = HashMap Text Value
- newtype DotNetTime = DotNetTime {
  - fromDotNetTime :: UTCTime
  }
- class FromJSON a where
  - parseJSON :: Value -> Parser a
- data Result a
  - = Error String
  - | Success a
- fromJSON :: FromJSON a => Value -> Result a
- class ToJSON a where
  - toJSON :: a -> Value
- withObject :: String -> (Object -> Parser a) -> Value -> Parser a
- withText :: String -> (Text -> Parser a) -> Value -> Parser a
- withArray :: String -> (Array -> Parser a) -> Value -> Parser a
- withNumber :: String -> (Number -> Parser a) -> Value -> Parser a
- withBool :: String -> (Bool -> Parser a) -> Value -> Parser a
- (.=) :: ToJSON a => Text -> a -> Pair
- (.:) :: FromJSON a => Object -> Text -> Parser a
- (.:?) :: FromJSON a => Object -> Text -> Parser (Maybe a)
- (.!=) :: Parser (Maybe a) -> a -> Parser a
- object :: [Pair] -> Value
- json :: Parser Value
- json' :: Parser Value

# How to use this library

This section contains basic information on the different ways to decode 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:

     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})

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 `Map` type to get a map of `Int`s.

     >>> decode "{\"foo\":1,\"bar\":2}" :: Maybe (Map String Int)
     Just (fromList [("bar",2),("foo",1)])

## Working with the 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.

## Decoding to a Haskell value

Any instance of `FromJSON` can be specified (but see the "Pitfalls" section here—Data.Aeson):

     λ> 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 `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 mixed-type object

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.

## Automatically decoding data types

If you don't want fine 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 `Typeable` and `Data`. Switch to the `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 may not always be what you'd naively expect:

     λ> data Foo = Foo Int Int deriving (Data,Typeable,Show)
     λ> encode (Foo 1 2)
     "[1,2]"

With this approach, it's best to treat the `decode` and `encode` functions as an isomorphism, and not to rely upon (or care about) the specific intermediate representation.

## Pitfalls

Note that the JSON standard only allows arrays or objects of things at the top-level. Since this library follows the standard, calling `decode` on an unsupported result type will typecheck, but will always "fail":

     >>> decode "1" :: Maybe Int
     Nothing
     >>> decode "1" :: Maybe String
     Nothing

So stick to objects (e.g. maps in Haskell) or arrays (lists or vectors in Haskell):

     >>> decode "[1,2,3]" :: Maybe [Int]
     Just [1,2,3]

When encoding to JSON you can encode anything that's an instance of `ToJSON`, and this may include simple types. So beware that this aspect of the API is not isomorphic. You can round-trip arrays and maps, but not simple values:

     >>> 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 `value` to parse non-top-level JSON values.

# Encoding and decoding

Encoding and decoding are each two-step processes.

- To encode a value, it is first converted to an abstract syntax tree (AST), using `ToJSON`. This generic representation is then encoded as bytes.
- When decoding a value, the process is reversed: the bytes are converted to an AST, then the `FromJSON` class is used to convert to the desired type.

For convenience, the `encode` and `decode` functions combine both steps for convenience.

decode :: FromJSON a => ByteString -> Maybe a

Efficiently deserialize a JSON value from a lazy `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 => ByteString -> Maybe a

Efficiently deserialize a JSON value from a lazy `ByteString`. If this fails due to incomplete or invalid input, `Nothing` is returned.

This function parses and performs conversion immediately. See `json'` for details.

eitherDecode :: FromJSON a => ByteString -> Either String a

Like `decode` but returns an error message when decoding fails.

eitherDecode' :: FromJSON a => ByteString -> Either String a

Like `decode'` but returns an error message when decoding fails.

encode :: ToJSON a => a -> ByteString

Efficiently serialize a JSON value as a lazy `ByteString`.

# Core JSON types

data Value

A JSON value represented as a Haskell value.

Constructors

Object !Object |
Array !Array |
String !Text |
Number !Number |
Bool !Bool |
Null |

Instances

Eq Value |
Show Value |
Typeable Value |
IsString Value |
NFData Value |
Hashable Value |
FromJSON Value |
ToJSON Value |

type Array = Vector Value

A JSON "array" (sequence).

type Object = HashMap Text Value

A JSON "object" (key/value map).

# Convenience types

newtype DotNetTime

A newtype wrapper for `UTCTime` that uses the same non-standard serialization format as Microsoft .NET, whose `System.DateTime` type is by default serialized to JSON as in the following example:

     /Date(1302547608878)/

The number represents milliseconds since the Unix epoch.

Constructors

DotNetTime |

Fields

fromDotNetTime :: UTCTime

Instances

Eq DotNetTime |
Ord DotNetTime |
Read DotNetTime |
Show DotNetTime |
Typeable DotNetTime |
FormatTime DotNetTime |
FromJSON DotNetTime |
ToJSON DotNetTime |

# Type conversion

class FromJSON a where

A type that can be converted from JSON, with the possibility of failure.

When writing an instance, use `empty`, `mzero`, or `fail` to make a conversion fail, e.g. if an `Object` is missing a required key, or the value is of the wrong type.

An example type and instance:

    {-# LANGUAGE OverloadedStrings #-}

    data Coord { x :: Double, y :: Double }

    instance FromJSON Coord where
       parseJSON (Object v) = Coord    <$>
                              v .: "x" <*>
                              v .: "y"

    -- A non-Object value is of the wrong type, so use mzero to fail.
       parseJSON _          = mzero

Note the use of the `OverloadedStrings` language extension which enables `Text` values to be written as string literals.

Instead of manually writing your `FromJSON` instance, there are three options to do it automatically:

- Data.Aeson.TH provides template-haskell functions which will derive an instance at compile-time. The generated instance is optimized for your type so will probably be more efficient than the following two options:
- Data.Aeson.Generic provides a generic `fromJSON` function that parses to any type which is an instance of `Data`.
- If your compiler has support for the `DeriveGeneric` and `DefaultSignatures` language extensions, `parseJSON` will have a default generic implementation.

To use this, simply add a `deriving `Generic`` clause to your datatype and declare a `FromJSON` instance for your datatype without giving a definition for `parseJSON`.

For example the previous example can be simplified to just:

    {-# LANGUAGE DeriveGeneric #-}

    import GHC.Generics

    data Coord { x :: Double, y :: Double } deriving Generic

    instance FromJSON Coord

Methods

parseJSON :: Value -> Parser a

Instances

FromJSON Bool |
FromJSON Char |
FromJSON Double |
FromJSON Float |
FromJSON Int |
FromJSON Int8 |
FromJSON Int16 |
FromJSON Int32 |
FromJSON Int64 |
FromJSON Integer |
FromJSON Word |
FromJSON Word8 |
FromJSON Word16 |
FromJSON Word32 |
FromJSON Word64 |
FromJSON () |
FromJSON ByteString |
FromJSON ByteString |
FromJSON Text |
FromJSON Number |
FromJSON Text |
FromJSON IntSet |
FromJSON ZonedTime |
FromJSON UTCTime |
FromJSON Value |
FromJSON DotNetTime |
FromJSON [Char] |
FromJSON a => FromJSON [a] |
FromJSON (Ratio Integer) |
FromJSON a => FromJSON (Maybe a) |
HasResolution a => FromJSON (Fixed a) |
FromJSON a => FromJSON (Dual a) |
FromJSON a => FromJSON (First a) |
FromJSON a => FromJSON (Last a) |
FromJSON a => FromJSON (IntMap a) |
(Ord a, FromJSON a) => FromJSON (Set a) |
(Eq a, Hashable a, FromJSON a) => FromJSON (HashSet a) |
FromJSON a => FromJSON (Vector a) |
(Vector Vector a, FromJSON a) => FromJSON (Vector a) |
(Storable a, FromJSON a) => FromJSON (Vector a) |
(Prim a, FromJSON a) => FromJSON (Vector a) |
(FromJSON a, FromJSON b) => FromJSON (Either a b) |
(FromJSON a, FromJSON b) => FromJSON (a, b) |
FromJSON v => FromJSON (Map String v) |
FromJSON v => FromJSON (Map ByteString v) |
FromJSON v => FromJSON (Map ByteString v) |
FromJSON v => FromJSON (Map Text v) |
FromJSON v => FromJSON (Map Text v) |
FromJSON v => FromJSON (HashMap String v) |
FromJSON v => FromJSON (HashMap ByteString v) |
FromJSON v => FromJSON (HashMap ByteString v) |
FromJSON v => FromJSON (HashMap Text v) |
FromJSON v => FromJSON (HashMap Text v) |
(FromJSON a, FromJSON b, FromJSON c) => FromJSON (a, b, c) |
(FromJSON a, FromJSON b, FromJSON c, FromJSON d) => FromJSON (a, b, c, d) |

data Result a

The result of running a `Parser`.

Constructors

Error String |
Success a |

Instances

Monad Result |
Functor Result |
Typeable1 Result |
MonadPlus Result |
Applicative Result |
Alternative Result |
Eq a => Eq (Result a) |
Show a => Show (Result a) |
Monoid (Result a) |
NFData a => NFData (Result a) |

fromJSON :: FromJSON a => Value -> Result a

Convert a value from JSON, failing if the types do not match.

class ToJSON a where

A type that can be converted to JSON.

An example type and instance:

    {-# LANGUAGE OverloadedStrings #-}

    data Coord { x :: Double, y :: Double }

    instance ToJSON Coord where
       toJSON (Coord x y) = object ["x" .= x, "y" .= y]

Note the use of the `OverloadedStrings` language extension which enables `Text` values to be written as string literals.

Instead of manually writing your `ToJSON` instance, there are three options to do it automatically:

- Data.Aeson.TH provides template-haskell functions which will derive an instance at compile-time. The generated instance is optimized for your type so will probably be more efficient than the following two options:
- Data.Aeson.Generic provides a generic `toJSON` function that accepts any type which is an instance of `Data`.
- If your compiler has support for the `DeriveGeneric` and `DefaultSignatures` language extensions (GHC 7.2 and newer), `toJSON` will have a default generic implementation.

To use the latter option, simply add a `deriving `Generic`` clause to your datatype and declare a `ToJSON` instance for your datatype without giving a definition for `toJSON`.

For example the previous example can be simplified to just:

    {-# LANGUAGE DeriveGeneric #-}

    import GHC.Generics

    data Coord { x :: Double, y :: Double } deriving Generic

    instance ToJSON Coord

Methods

toJSON :: a -> Value

Instances

ToJSON Bool |
ToJSON Char |
ToJSON Double |
ToJSON Float |
ToJSON Int |
ToJSON Int8 |
ToJSON Int16 |
ToJSON Int32 |
ToJSON Int64 |
ToJSON Integer |
ToJSON Word |
ToJSON Word8 |
ToJSON Word16 |
ToJSON Word32 |
ToJSON Word64 |
ToJSON () |
ToJSON ByteString |
ToJSON ByteString |
ToJSON Text |
ToJSON Number |
ToJSON Text |
ToJSON IntSet |
ToJSON ZonedTime |
ToJSON UTCTime |
ToJSON Value |
ToJSON DotNetTime |
ToJSON [Char] |
ToJSON a => ToJSON [a] |
ToJSON (Ratio Integer) |
ToJSON a => ToJSON (Maybe a) |
HasResolution a => ToJSON (Fixed a) |
ToJSON a => ToJSON (Dual a) |
ToJSON a => ToJSON (First a) |
ToJSON a => ToJSON (Last a) |
ToJSON a => ToJSON (IntMap a) |
ToJSON a => ToJSON (Set a) |
ToJSON a => ToJSON (HashSet a) |
ToJSON a => ToJSON (Vector a) |
(Vector Vector a, ToJSON a) => ToJSON (Vector a) |
(Storable a, ToJSON a) => ToJSON (Vector a) |
(Prim a, ToJSON a) => ToJSON (Vector a) |
(ToJSON a, ToJSON b) => ToJSON (Either a b) |
(ToJSON a, ToJSON b) => ToJSON (a, b) |
ToJSON v => ToJSON (Map String v) |
ToJSON v => ToJSON (Map ByteString v) |
ToJSON v => ToJSON (Map ByteString v) |
ToJSON v => ToJSON (Map Text v) |
ToJSON v => ToJSON (Map Text v) |
ToJSON v => ToJSON (HashMap String v) |
ToJSON v => ToJSON (HashMap ByteString v) |
ToJSON v => ToJSON (HashMap ByteString v) |
ToJSON v => ToJSON (HashMap Text v) |
ToJSON v => ToJSON (HashMap Text v) |
(ToJSON a, ToJSON b, ToJSON c) => ToJSON (a, b, c) |
(ToJSON a, ToJSON b, ToJSON c, ToJSON d) => ToJSON (a, b, c, d) |

# Inspecting ``Value`s`

withObject :: String -> (Object -> Parser a) -> Value -> Parser a

`withObject expected f value` applies `f` to the `Object` when `value` is an `Object` and fails using ``typeMismatch` expected` otherwise.

withText :: String -> (Text -> Parser a) -> Value -> Parser a

`withObject expected f value` applies `f` to the `Text` when `value` is a `String` and fails using ``typeMismatch` expected` otherwise.

withArray :: String -> (Array -> Parser a) -> Value -> Parser a

`withObject expected f value` applies `f` to the `Array` when `value` is an `Array` and fails using ``typeMismatch` expected` otherwise.

withNumber :: String -> (Number -> Parser a) -> Value -> Parser a

`withObject expected f value` applies `f` to the `Number` when `value` is a `Number` and fails using ``typeMismatch` expected` otherwise.

withBool :: String -> (Bool -> Parser a) -> Value -> Parser a

`withObject expected f value` applies `f` to the `Bool` when `value` is a `Bool` and fails using ``typeMismatch` expected` otherwise.

# Constructors and accessors

(.=) :: ToJSON a => Text -> a -> Pair

Construct a `Pair` from a key and a value.

(.:) :: FromJSON a => Object -> Text -> Parser a

Retrieve the value associated with the given key of an `Object`. The result is `empty` if the key is not present or the value cannot be converted to the desired type.

This accessor is appropriate if the key and value must be present in an object for it to be valid. If the key and value are optional, use '(.:?)' instead.

(.:?) :: FromJSON a => Object -> Text -> Parser (Maybe a)

Retrieve the value associated with the given key of an `Object`. The result is `Nothing` if the key is not present, or `empty` if the value cannot be converted to the desired type.

This accessor is most useful if the key and value can be absent from an object without affecting its validity. If the key and value are mandatory, use '(.:)' instead.

(.!=) :: Parser (Maybe a) -> a -> Parser a

Helper for use in combination with `.:?` to provide default values for optional JSON object fields.

This combinator is most useful if the key and value can be absent from an object without affecting its validity and we know a default value to assign in that case. If the key and value are mandatory, use '(.:)' instead.

Example usage:

     v1 <- o .:? "opt_field_with_dfl" .!= "default_val"
     v2 <- o .:  "mandatory_field"
     v3 <- o .:? "opt_field2"

object :: [Pair] -> Value

Create a `Value` from a list of name/value `Pair`s. If duplicate keys arise, earlier keys and their associated values win.

# Parsing

json :: Parser Value

Parse a top-level JSON value. This must be either an object or an array, per RFC 4627.

The conversion of a parsed value to a Haskell value is deferred until the Haskell value is needed. This may improve performance if only a subset of the results of conversions are needed, but at a cost in thunk allocation.

json' :: Parser Value

Parse a top-level JSON value. This must be either an object or an array, per RFC 4627.

This is a strict version of `json` which avoids building up thunks during parsing; it performs all conversions immediately. Prefer this version if most of the JSON data needs to be accessed.