json-validation-0.1.0.0: Lightweight combinators for validating JSON

Safe HaskellNone
LanguageHaskell2010

Data.Aeson.Validation

Contents

Description

JSON schema validation.

Synopsis

Schema validation

data Schema Source #

An opaque JSON Schema.

Instances

Fractional Schema Source #

The Fractional instance only defines one function; all other Fractional functions call error.

  1. fromRational is provided for floating point-literal syntax.

    fromRational = theNumber . fromRational
    

    Examples:

    >>> schema 1.5 (Number 1.5)
    True
    
    >>> schema 2.5 (Number 2.500000001)
    True
    
Num Schema Source #

The Num instance only defines two functions; all other Num functions call error.

  1. fromInteger is provided for integer-literal syntax.

    fromInteger = theInteger
    

    Examples:

    >>> schema 1 (Number 1)
    True
    
  2. negate s succeeds whenever s fails.

    negate is its own inverse:

    negate . negate = id
    

    Examples:

    >>> schema (negate bool) (Bool True)
    False
    
    >>> schema (negate bool) (String "foo")
    True
    
IsString Schema Source #

fromString is provided for string-literal syntax.

fromString = theString . pack

Examples:

>>> schema "foo" (String "foo")
True

Methods

fromString :: String -> Schema #

Semigroup Schema Source #

The <> operator is used to create a sum Schema that, when applied to a Value, first tries the left Schema, then falls back on the right one if the left one fails.

schema (s1 <> s2) val = schema s1 val || schema s2 val

For validate, if any Schemas emits no violations, then no violations are emitted. Otherwise, all violations are emitted.

Examples:

>>> validate (bool <> string) (Bool True)
[]
>>> validate (bool <> string) (String "foo")
[]
>>> validate (bool <> string) (Number 1)
["expected a bool but found a number","expected a string but found a number"]

schema :: Schema -> Value -> Bool Source #

Does the Value satisfy the Schema?

schema s v = null (validate s v)

validate :: Schema -> Value -> [Text] Source #

Validate a Value with a Schema and emit schema violations as Text.

Boolean schemas

bool :: Schema Source #

Any Bool.

Examples

>>> schema bool (Bool True)
True
>>> schema bool (Bool False)
True

true :: Schema Source #

Bool True.

Examples

>>> schema true (Bool True)
True
>>> schema true (Bool False)
False

false :: Schema Source #

Bool False.

Examples

>>> schema false (Bool True)
False
>>> schema false (Bool False)
True

Number schemas

number :: Schema Source #

Any Number.

Examples

>>> schema number (Number 1.0)
True

theNumber :: Scientific -> Schema Source #

An approximate Number, with a relative tolerance of 1^-9 (the smaller number must be within 0.0000001% of the larger number).

You may use a floating point literal instead.

Here is how you'd implement theNumber using someNumber, in case you want to use a different tolerance:

theNumber :: Scientific -> Schema
theNumber n = someNumber (isClose n)
  where
    isClose :: Scientific -> Scientific -> Bool
    isClose a b = abs (a-b) <= 1e-9 * max (abs a) (abs b)

Examples

>>> schema (theNumber 1.5) (Number 1.5)
True
>>> schema 2.5 (Number 2.500000001)
True

someNumber :: A schema => (Scientific -> Bool) -> schema Source #

Some Number.

The unexported A typeclass exists to overload someNumber with precicely two possible types:

someNumber :: (Scientific -> Bool) -> Schema
someNumber :: (Scientific -> Bool) -> Text -> Schema

The optional Text argument is used for error reporting if validation fails.

Examples

>>> schema (someNumber (> 5)) (Number 6)
True
>>> validate (someNumber (> 5) "greater than 5") (Number 4)
["failed predicate: greater than 5"]
>>> validate (someNumber (> 5)) (Number 4)
["failed predicate"]

integer :: Schema Source #

Any integer Number.

Examples

>>> schema integer (Number 1.0)
True
>>> schema integer (Number 1.5)
False

theInteger :: Integer -> Schema Source #

An exact integer Number.

You may use an integer literal instead.

Examples

>>> schema (theInteger 1) (Number 1)
True
>>> schema 1 (Number 2)
False

someInteger :: A schema => (Integer -> Bool) -> schema Source #

Some integer Number.

The unexported A typeclass exists to overload someInteger with precicely two possible types:

someInteger :: (Integer -> Bool) -> Schema
someInteger :: (Integer -> Bool) -> Text -> Schema

The optional Text argument is used for error reporting if validation fails.

Examples

>>> schema (someInteger (> 5)) (Number 6.0)
True
>>> validate (someInteger (> 5) "greater than 5") (Number 6.5)
["failed predicate: greater than 5"]
>>> validate (someInteger (> 5)) (Number 6.5)
["failed predicate"]

String schemas

string :: Schema Source #

Any String.

Examples

>>> schema string (String "foo")
True

theString :: Text -> Schema Source #

An exact String.

You may use a string literal instead (requires -XOverloadedStrings).

Examples

>>> schema (theString "foo") (String "foo")
True
>>> schema "foo" (String "bar")
False

someString :: A schema => (Text -> Bool) -> schema Source #

Some String.

The unexported A typeclass exists to overload someString with precicely two possible types:

someString :: (Text -> Bool) -> Schema
someString :: (Text -> Bool) -> Text -> Schema

The optional Text argument is used for error reporting if validation fails.

Examples

>>> schema (someString (\s -> Text.length s > 5)) (String "foobar")
True
>>> schema (someString (\s -> Text.length s > 5)) (String "foo")
False

regex :: Text -> Schema Source #

A String that matches a regular expression.

Examples

>>> schema (regex "a+b") (String "xaaabx")
True
>>> schema (regex "c{2}") (String "cd")
False

datetime :: Schema Source #

A String in ISO 8601 format.

Examples

>>> schema datetime (String "2000-01-01T00:00:00.000Z")
True

Object schemas

data Field Source #

An opaque object Field.

Create a Field with .: or .:?, and bundle it into a Schema using object or object'

data Path Source #

An arbitrarily deep non-empty Path into an Object, created with either string-literal or list-literal syntax.

Beware: the IsList instance is partial; [] is not allowed and will call error.

Examples

>>> "foo" :: Path
["foo"]
>>> ["foo", "bar"] :: Path
["foo","bar"]
>>> [] :: Path
*** Exception: Data.Aeson.Validation.Path.fromList: empty list

Instances

IsList Path Source #

Paths created with [] syntax must be non-empty.

Associated Types

type Item Path :: * #

Methods

fromList :: [Item Path] -> Path #

fromListN :: Int -> [Item Path] -> Path #

toList :: Path -> [Item Path] #

Eq Path Source # 

Methods

(==) :: Path -> Path -> Bool #

(/=) :: Path -> Path -> Bool #

Ord Path Source # 

Methods

compare :: Path -> Path -> Ordering #

(<) :: Path -> Path -> Bool #

(<=) :: Path -> Path -> Bool #

(>) :: Path -> Path -> Bool #

(>=) :: Path -> Path -> Bool #

max :: Path -> Path -> Path #

min :: Path -> Path -> Path #

Show Path Source # 

Methods

showsPrec :: Int -> Path -> ShowS #

show :: Path -> String #

showList :: [Path] -> ShowS #

IsString Path Source #

A singleton Path.

Methods

fromString :: String -> Path #

type Item Path Source # 
type Item Path = Text

object :: [Field] -> Schema Source #

An Object, possibly with additional fields.

To match any Object, use object [].

Examples

>>> let fields = ["foo" .: number]
>>> let values = ["foo" .= Number 1, "bar" .= Bool True]
>>> schema (object fields) (Object values)
True
>>> let fields = [["foo", "bar"] .: number]
>>> let values = ["foo" .= Object ["bar" .= Number 1]]
>>> schema (object fields) (Object values)
True

object' :: [Field] -> Schema Source #

An Object with no additional fields.

The ' mark means "strict" as in foldl', because object' matches Objects more strictly than object.

Examples

>>> let fields = ["foo" .: number]
>>> let values = ["foo" .= Number 1]
>>> schema (object' fields) (Object values)
True
>>> let fields = ["foo" .: number]
>>> let values = ["foo" .= Number 1, "bar" .= Bool True]
>>> schema (object' fields) (Object values)
False
>>> let fields = [["foo", "bar"] .: number]
>>> let values = ["foo" .= Object ["bar" .= Number 1]]
>>> schema (object' fields) (Object values)
True
>>> let fields = [["foo", "bar"] .: number]
>>> let values = ["foo" .= Object ["bar" .= Number 1], "baz" .= Bool True]
>>> schema (object' fields) (Object values)
False

(.:) :: Path -> Schema -> Field infixr 5 Source #

A required Field.

(.:?) :: Path -> Schema -> Field infixr 5 Source #

An optional Field.

Array schemas (homogenous lists)

array :: Schema -> Schema Source #

A "homogenous" Array of any size.

The array need not be truly homogenous; it simply has the same Schema applied to each element. However, the Schema could be anything, or composed of many alternatives using <>.

Examples

>>> schema (array bool) (Array [Bool True, Bool False])
True
>>> schema (array anything) (Array [Bool True, String "foo"])
True
>>> schema (array integer) (Array [Number 1.5])
False

sizedArray :: Int -> Int -> Schema -> Schema Source #

A sized (inclusive), "homogenous" (see note above) Array. Use minBound or maxBound for an unbounded edge.

Examples

>>> schema (sizedArray 1 2 bool) (Array [Bool True])
True
>>> schema (sizedArray 1 2 bool) (Array [Bool True, Bool True, Bool False])
False

Set schemas (homogenous, unique lists)

set :: Schema -> Schema Source #

A "homogenous" (see note above), unique Array of any size.

Examples

>>> schema (set bool) (Array [Bool True])
True
>>> schema (set bool) (Array [Bool True, Bool True])
False

sizedSet :: Int -> Int -> Schema -> Schema Source #

A sized (inclusive), "homogenous" (see note above), unique Array. Use minBound or maxBound for an unbounded edge.

Examples

>>> schema (sizedSet 1 1 string) (Array [String "foo"])
True
>>> schema (sizedSet 1 1 string) (Array [String "foo", String "bar"])
False

Tuple schemas (heterogeneous lists)

tuple :: [Schema] -> Schema Source #

A heterogeneous Array exactly as long as the given list of Schemas.

Examples

>>> schema (tuple [bool, string]) (Array [Bool True, String "foo"])
True

Miscellaneous schemas

anything :: Schema Source #

Any Value whatsoever, including Null.

Examples

>>> schema anything (Bool True)
True
>>> schema anything Null
True

nullable :: Schema -> Schema Source #

Modify a Schema to additionally accept Null.

nullable is idempotent:

nullable = nullable . nullable

Examples

>>> schema (nullable bool) (Bool True)
True
>>> schema (nullable bool) Null
True