aeson-compat-0.3.6: Compatibility layer for aeson

Copyright(C) 2015 Oleg Grenrus
LicenseBSD3
MaintainerOleg Grenrus <oleg.grenrus@iki.fi>
Safe HaskellNone
LanguageHaskell2010

Data.Aeson.Compat

Contents

Description

Compatibility notices

  • decode etc. work as in aeson >=0.9
  • but it is generalised to work in any MonadThrow (that is extra)
  • .:? works as in aeson <0.10
  • .:! works as .:? in aeson ==0.10
  • Orphan instances FromJSON Day and FromJSON LocalTime for aeson <0.10
  • Encoding related functionality is not added. It's present only with aeson >=0.10

Synopsis

Encoding and decoding

Direct encoding

decode :: (FromJSON a, MonadThrow m) => ByteString -> m a Source

Like original decode but in arbitrary MonadThrow.

Parse a top-level JSON value, i.e. also strings, numbers etc.

decode' :: (FromJSON a, MonadThrow m) => ByteString -> m a Source

Like original decode' but in arbitrary MonadThrow.

newtype AesonException Source

Exception thrown by decode - family of functions in this module.

Constructors

AesonException String 

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.

This is implemented in terms of the ToJSON class's toEncoding method.

Variants for strict bytestrings

decodeStrict :: (FromJSON a, MonadThrow m) => ByteString -> m a Source

Like original decodeStrict but in arbitrary MonadThrow.

decodeStrict' :: (FromJSON a, MonadThrow m) => ByteString -> m a Source

Like original decodeStrict' but in arbitrary MonadThrow.

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

Like decodeStrict but returns an error message when decoding fails.

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

Like decodeStrict' but returns an error message when decoding fails.

Core JSON types

data Value :: *

A JSON value represented as a Haskell value.

data Encoding :: *

An encoding of a JSON value.

fromEncoding :: Encoding -> Builder

Acquire the underlying bytestring builder.

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

Acquire the underlying value.

Type conversion

class FromJSON a where

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

In many cases, you can get the compiler to generate parsing code for you (see below). To begin, let's cover writing an instance by hand.

There are various reasons a conversion could fail. For example, an Object could be missing a required key, an Array could be of the wrong size, or a value could be of an incompatible type.

The basic ways to signal a failed conversion are as follows:

  • empty and mzero work, but are terse and uninformative
  • fail yields a custom error message
  • typeMismatch produces an informative message for cases when the value encountered is not of the expected type

An example type and instance:

-- Allow ourselves to write Text literals.
{-# LANGUAGE OverloadedStrings #-}

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

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

  -- We do not expect a non-Object value here.
  -- We could use mzero to fail, but typeMismatch
  -- gives a much more informative error message.
  parseJSON invalid    = typeMismatch "Coord" invalid

Instead of manually writing your FromJSON instance, there are two 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:
  • The compiler can provide a default generic implementation for parseJSON.

To use the second, 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 = Coord { x :: Double, y :: Double } deriving Generic

instance FromJSON Coord

If DefaultSignatures doesn't give exactly the results you want, you can customize the generic decoding with only a tiny amount of effort, using genericParseJSON with your preferred Options:

instance FromJSON Coord where
    parseJSON = genericParseJSON defaultOptions

Minimal complete definition

Nothing

Methods

parseJSON :: Value -> Parser a

data Result a :: * -> *

The result of running a Parser.

Constructors

Error String 
Success 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:

-- Allow ourselves to write Text literals.
{-# LANGUAGE OverloadedStrings #-}

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

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

  toEncoding (Coord x y) = pairs ("x" .= x <> "y" .= y)

Instead of manually writing your ToJSON instance, there are two 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:
  • The compiler can provide a default generic implementation for toJSON.

To use the second, simply add a deriving Generic clause to your datatype and declare a ToJSON instance for your datatype without giving definitions for toJSON or toEncoding.

For example, the previous example can be simplified to a more minimal instance:

{-# LANGUAGE DeriveGeneric #-}

import GHC.Generics

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

instance ToJSON Coord where
    toEncoding = genericToEncoding defaultOptions

Why do we provide an implementation for toEncoding here? The toEncoding function is a relatively new addition to this class. To allow users of older versions of this library to upgrade without having to edit all of their instances or encounter surprising incompatibilities, the default implementation of toEncoding uses toJSON. This produces correct results, but since it performs an intermediate conversion to a Value, it will be less efficient than directly emitting an Encoding. Our one-liner definition of toEncoding above bypasses the intermediate Value.

If DefaultSignatures doesn't give exactly the results you want, you can customize the generic encoding with only a tiny amount of effort, using genericToJSON and genericToEncoding with your preferred Options:

instance ToJSON Coord where
    toJSON     = genericToJSON defaultOptions
    toEncoding = genericToEncoding defaultOptions

Minimal complete definition

Nothing

Methods

toJSON :: a -> Value

Convert a Haskell value to a JSON-friendly intermediate type.

toEncoding :: a -> Encoding

Encode a Haskell value as JSON.

The default implementation of this method creates an intermediate Value using toJSON. This provides source-level compatibility for people upgrading from older versions of this library, but obviously offers no performance advantage.

To benefit from direct encoding, you must provide an implementation for this method. The easiest way to do so is by having your types implement Generic using the DeriveGeneric extension, and then have GHC generate a method body as follows.

instance ToJSON Coord where
    toEncoding = genericToEncoding defaultOptions

class KeyValue kv where

A key-value pair for encoding a JSON object.

Methods

(.=) :: ToJSON v => Text -> v -> kv infixr 8

Generic JSON classes and options

class GFromJSON f where

Class of generic representation types (Rep) that can be converted from JSON.

Methods

gParseJSON :: Options -> Value -> Parser (f a)

This method (applied to defaultOptions) is used as the default generic implementation of parseJSON.

class GToJSON f where

Class of generic representation types (Rep) that can be converted to JSON.

Methods

gToJSON :: Options -> f a -> Value

This method (applied to defaultOptions) is used as the default generic implementation of toJSON.

class GToEncoding f where

Class of generic representation types (Rep) that can be converted to a JSON Encoding.

Methods

gToEncoding :: Options -> f a -> Encoding

This method (applied to defaultOptions) can be used as the default generic implementation of toEncoding.

genericToJSON :: (Generic a, GToJSON (Rep a)) => Options -> a -> Value

A configurable generic JSON creator. This function applied to defaultOptions is used as the default for toJSON when the type is an instance of Generic.

genericToEncoding :: (Generic a, GToEncoding (Rep a)) => Options -> a -> Encoding

A configurable generic JSON encoder. This function applied to defaultOptions is used as the default for toEncoding when the type is an instance of Generic.

genericParseJSON :: (Generic a, GFromJSON (Rep a)) => Options -> Value -> Parser a

A configurable generic JSON decoder. This function applied to defaultOptions is used as the default for parseJSON when the type is an instance of Generic.

Inspecting Values

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

withText 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

withArray 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

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

withScientific :: String -> (Scientific -> Parser a) -> Value -> Parser a

withScientific expected f value applies f to the Scientific number when value is a Number. and fails using typeMismatch expected otherwise.

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

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

Constructors and accessors

data Series :: *

A series of values that, when encoded, should be separated by commas. Since 0.11.0.0, the .= operator is overloaded to create either (Text, Value) or Series. You can use Series when encoding directly to a bytestring builder as in the following example:

toEncoding (Person name age) = pairs ("name" .= name <> "age" .= age)

Instances

Monoid Series 
Semigroup Series 

pairs :: Series -> Encoding

Encode a series of key/value pairs, separated by commas.

foldable :: (Foldable t, ToJSON a) => t a -> Encoding

Encode a Foldable as a JSON array.

(.:) :: 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) Source

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.

This operator is consistent in aeson >=0.7 && <0.11

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

Like .:?, but the resulting parser will fail, if the key is present but is Null.

(.!=) :: 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 Pairs. If duplicate keys arise, earlier keys and their associated values win.

Parsing

json :: Parser Value

Parse a top-level JSON value.

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.

This function is an alias for value. In aeson 0.8 and earlier, it parsed only object or array types, in conformance with the now-obsolete RFC 4627.

json' :: Parser Value

Parse a top-level JSON value.

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.

This function is an alias for value'. In aeson 0.8 and earlier, it parsed only object or array types, in conformance with the now-obsolete RFC 4627.

value :: Parser Value

Parse any JSON value. You should usually json in preference to this function, as this function relaxes the object-or-array requirement of RFC 4627.

In particular, be careful in using this function if you think your code might interoperate with Javascript. A naïve Javascript library that parses JSON data using eval is vulnerable to attack unless the encoded data represents an object or an array. JSON implementations in other languages conform to that same restriction to preserve interoperability and security.

value' :: Parser Value

Strict version of value. See also json'.

data Parser a :: * -> *

A JSON parser.