git.iko.soy/typeable/schematic
on 0.1.2.0 help sign in
113 lines 4.1 kB history raw
1# schematic
3[![Build Status](https://travis-ci.org/dredozubov/schematic.svg?branch=master)](https://travis-ci.org/dredozubov/schematic)
5## Goal
7The goal of the library is to provide a type-safe transport layer for serializing and validating JSON. It can be thought of as a subset of [json-schema](http://json-schema.org), which is basically a specification of a JSON document. The other goal is getting as much as possible from this specification for free. Right now the following bits are prototyped:
9* All the instantiations of transport types are structurally follow the schema provided by user
10* Serializers are generic, so they follow from the type-level schema and the're supposed to have roundtrip property by implementation.
11* Runtime value validators are generated from the schema. Validation errors are reported as a pairs of a json-path to the element and an error message.
12* There are migrations. It's possible to describe a series of migrations to the schema and have all the machinery deserialize a user specified version of the schema if there's a few versions.
14## TODO
16* Making a migration system more usable. It should allow it to be used as a json safecopy solution minus the boilerplate.
17* Describing versioned http endpoints
18* Schema pretty-printing
19* More validators
20* Overall user friendliness
22Be aware that library is experimental and subject to change a lot. The current state can be viewed as a prototype.
24## Installation
26* Install [Stack](https://github.com/commercialhaskell/stack)
28```
29$ stack install schematic
30```
32## Basic Examples
34```
35type SchemaExample
36 = SchemaObject
37 '[ '("foo", SchemaArray '[AEq 1] (SchemaNumber '[NGt 10]))
38 , '("bar", SchemaOptional (SchemaText '[TEnum '["foo", "bar"]]))]
39```
41* This schema says that's structurally it's a JSON object which has two field:
42 * field "foo" contains an of numeric values
43 * field "bar" contains an optional textual value
44* It also allows you to validate the JSON:
45 * an array in "foo" must contain exactly one element
46 * that element must be strictly greater than 10
47 * value of bar must be either "foo" or "bar" if presented
50This one is valid for example
52```
53schemaJson :: ByteString
54schemaJson = "{\"foo\": [13], \"bar\": null}"
55```
57Also valid
59```
60schemaJson :: ByteString
61schemaJson = "{\"foo\": [13], \"bar\": \"bar\"}"
62```
64it can be parsed and validated like this:
66```
67decodeAndValidateJson schemaJson :: ParseResult (JsonRepr SchemaExample)
68```
70`ParseResult` type encodes three possible situations:
71* json is structurally consistent with schema and runtime validations passed
72* json source is malformed or doesn't correspond to schema structurally
73* some of the runtime validations failed, but it's structurally correct
75I t's build a transport layer representation of that data that can be tranversed and transformed to whatever internal types user has. Transport layer represetation looks like this. Type parameter is the schema itself.
78```
79jsonExample :: JsonRepr SchemaExample
80jsonExample = ReprObject $
81 FieldRepr (ReprArray [ReprNumber 12])
82 :& FieldRepr (ReprOptional (Just (ReprText "bar")))
83 :& RNil
84```
86## Migrations
88It's possible to represent schema changes as a migration, which describes a series of json-path/change pairs. Migrations can be applied in succession.
90This piece of code will apply a migration to the schema, then decodes and validates the latest version.
92```
93type SchemaExample
94 = SchemaObject
95 '[ '("foo", SchemaArray '[AEq 1] (SchemaNumber '[NGt 10]))
96 , '("bar", SchemaOptional (SchemaText '[TEnum '["foo", "bar"]]))]
98type TestMigration =
99 'Migration "test_revision"
100 '[ 'Diff '[ 'PKey "bar" ] ('Update ('SchemaText '[]))
101 , 'Diff '[ 'PKey "foo" ] ('Update ('SchemaNumber '[])) ]
103type VS = 'Versioned SchemaExample '[ TestMigration ]
105schemaJsonTopVersion :: ByteString
106schemaJsonTopVersion = "{ \"foo\": 42, \"bar\": \"bar\" }"
107```
109It's possible to decode the latest version like this:
111```
112decodeAndValidateJson schemaJsonTopVersion :: ParseResult (JsonRepr (TopVersion (AllVersions VS)))
113```