Description
JSON Schema is a vocabulary for annotating and validating data in JavaScript. More about JSON Schema on json-schema.org
A schema can be used from the simplest description of the data type of a value:
{type: "string"}
"Foo"
To an object with both rules for required fields and validation rules for single values:
{type: "object",properties: {txt: { type: 'string', minLength: 5 },num: { type: 'number', maximum: 100 },},required: ['txt']}
{"txt": "abcde","num": 123}
Using schema with DataContext
Since a DataContext (used in isolation or through the use of Form.Handler) supports JSON Schema, these two examples will result in the same validation for the user:
<Form.Handler data={user}><Field.String path="/name" label="Name" minLength={3} required /><Field.Email path="/email" label="E-mail" required /><Field.Numberpath="/birthyear"label="Birth year"minimum={1900}maximum={2023}required/></Form.Handler>
const schema = {type: 'object',properties: {name: { type: 'string', minLength: 3 },email: { type: 'string' },birthyear: { type: 'number', minimum: 1900, maximum: 2023 }},required: ['name', 'email', 'birthyear'],}<Form.Handler data={user} schema={schema}><Field.String path="/name" label="Name" /><Field.Email path="/email" label="E-mail" /><Field.Number path="/birthyear" label="Birth year" /></Form.Handler>
This makes it possible to create a uniform, testable description and requirements specification for the data, which can be tested independently of frontend code, and used across systems, e.g. frontend and backend.
Complex schemas
In addition to basic validation as in the example above, JSON Schema can be used for more complex. Examples of definitions supported by the standard are:
- Requirement that the object must not have other properties than those defined in
properties
. - Nested data structures and combinations of objects and arrays with rules for array elements (fixed or repetitive elements).
- Regular expressions for the syntax of individual values.
- Enum (a set of valid values).
- Rules for the number of elements in arrays.
- Rules for the number of properties in objects.
- Predefined format rules (eg 'uri', 'email' and 'hostname').
- Logical operators such as 'not', 'oneOf', 'allOf' and 'anyOf' which can be filled with rules for all or part of the data set.
- Rule set based on the content of values (if-then-else).
- Rules (sub-schemas) that become applicable if a given value is present.
- Reuse within the definition, such as one and the same object structure being used as a definition for several locations in a structure.
To learn more about what is possible with the JSON Schema standard, see json-schema.org.
Demos
Schema for single field
<Field.String schema={{ type: 'string', minLength: 5, }} />
Schema for a whole data set
<Form.Handler data={{ address: 'Prefilled address', }} schema={{ type: 'object', properties: { name: { type: 'string', minLength: 2, }, address: { type: 'string', minLength: 3, }, }, required: ['name', 'address'], }} > <Card spacing="small" bottom="small"> <Form.MainHeading>Company information</Form.MainHeading> <Field.String path="/name" label="Name" /> <Field.String path="/address" label="Address" /> </Card> <Form.SubmitButton /> </Form.Handler>
Schema with if-rule
<Form.Handler data={{}} schema={{ type: 'object', properties: { name: { type: 'string', }, customerType: { type: 'string', enum: ['corporate', 'private'], }, companyName: { type: 'string', }, }, if: { properties: { customerType: { enum: ['corporate'], }, }, required: ['customerType'], }, then: { required: ['name', 'companyName'], }, else: { required: ['name'], }, }} > <Card spacing="small" bottom="small"> <Form.MainHeading>Customer information</Form.MainHeading> <Field.String path="/name" label="Name" /> <Field.String path="/customerType" label="Customer type (corporate or private)" /> <Field.String path="/companyName" label="Company name (required for corporate customers)" /> </Card> <Form.SubmitButton /> </Form.Handler>
Dependant list schema
Becoming a new customer, this form requires at least one normal account to be added, unless the customer opens a BSU account, then normal accounts can still be added, but is optional.
<Form.Handler data={{ accounts: [{}], }} schema={{ type: 'object', definitions: { account: { type: 'object', properties: { accountNumber: { type: 'string', pattern: '^[0-9]{11}$', }, alias: { type: 'string', minLength: 2, maxLength: 32, }, }, required: ['accountNumber'], }, }, properties: { name: { type: 'string', }, email: { type: 'string', }, phone: { type: 'string', }, accounts: { type: 'array', items: { $ref: '#/definitions/account', }, }, bsuAccount: { $ref: '#/definitions/account', }, }, oneOf: [ { properties: { accounts: { type: 'array', minItems: 1, }, }, }, { properties: { accounts: { type: 'array', minItems: 0, }, bsuAccount: { type: 'object', required: ['accountNumber'], }, }, required: ['bsuAccount'], }, ], }} > <Flex.Vertical spacing="small"> <Form.MainHeading>Customer information</Form.MainHeading> <Card spacing="small"> <Field.String path="/name" label="Name" /> <Field.Email path="/email" label="E-mail" /> <Field.PhoneNumber path="/phone" label="Phone number" /> </Card> <Form.MainHeading>Accounts</Form.MainHeading> <Card spacing="small"> <Form.SubHeading>Standard accounts</Form.SubHeading> <Iterate.Array path="/accounts"> <Flex.Horizontal align="flex-end"> <Field.BankAccountNumber elementPath="/accountNumber" label="Account number" /> <Field.String elementPath="/alias" label="Alias" width="medium" /> <Iterate.ArrayRemoveElementButton icon={TrashIcon} /> </Flex.Horizontal> </Iterate.Array> <Iterate.ArrayPushButton icon="add" icon_position="left" text="Add account" path="/accounts" pushValue={{}} size="medium" /> <Form.SubHeading>BSU Account</Form.SubHeading> <Field.BankAccountNumber path="/bsuAccount/accountNumber" label="Account number" /> <Field.String path="/bsuAccount/alias" label="Alias" /> </Card> <Form.SubmitButton /> </Flex.Vertical> </Form.Handler>