JSON Schema, shortly
Yanick Champoux (@yenzie)
february 19th, 2018
JSON Schema is a neat way to describe or prescribe structural expectations of JSON documents (or, indeed, any data structure, let it be a JavaScript plain object or the equivalent in another language). But JSON schemas are themselves JSON documents and, while machines love a good ol’ JSON format, let’s face it: for us humans it’s a lengthy, picky, and mildly onerous format to write and read.
Fortunately, there are many ways to craft JSON schemas while circumventing most of its JSON-born tediousness. Let me show you a few of them.
We’ll need a volunteer from the audience…
Talking abstract is boring. Let’s work on a real yet comfortably-sized example:
{
"$id": "http://aotds/battle/ship",
"title": "Ship",
"definitions": {
"course": {
"type": "array",
"description": "projected movement for new turn",
"items": {
"type": "object",
"properties": {
"heading": {
"$ref": "#/definitions/heading"
},
"coords": {
"$ref": "#/definitions/coords"
}
}
}
},
"heading": {
"type": "number",
"description": "forward angle of the object",
"minimum": 0,
"maximum": 12
},
"coords": {
"type": "array",
"items": {
"type": "number"
},
"minItems": 2,
"maxItems": 2
},
"velocity": {
"description": "speed of the object",
"type": "number",
"minimum": 0
}
},
"type": "object",
"properties": {
"navigation": {
"type": "object",
"properties": {
"heading": {
"$ref": "#/definitions/heading"
},
"course": {
"$ref": "#/definitions/course"
},
"coords": {
"$ref": "#/definitions/coords"
},
"velocity": {
"$ref": "#/definitions/velocity"
}
}
}
}
}
As we can see, it’s in no way the most horrendous format we’ll ever deal with. Still, it’s not something I’d expect anyone to peruse — or worse, type out — with glee.
Sidestep the problem to the left…
JSON is a pain to write mostly because of its exuberant quoting. And then there is this thrice darned phobia of trailing commas. But while JSON is the canonical format for JSON schemas, nobody said that you had to write or read it in that format. As long it’s possible to convert to and fro, there is no good reason not to use a different format for soft, tender human consumption.
Just a tad
To stay JSON formal, but loosen its tie a little bit, there is JSON5.
{
"$id": "http://aotds/battle/ship",
"title": "Ship",
"definitions": {
"course": {
"type": "array",
"description": "projected movement for new turn",
"items": {
"type": "object",
"properties": {
"heading": {
"$ref": "#/definitions/heading"
},
"coords": {
"$ref": "#/definitions/coords"
}
}
}
},
"heading": {
"type": "number",
"description": "forward angle of the object",
"minimum": 0,
"maximum": 12
},
"coords": {
"type": "array",
"items": {
"type": "number"
},
"minItems": 2,
"maxItems": 2
},
"velocity": {
"description": "speed of the object",
"type": "number",
"minimum": 0
}
},
"type": "object",
"properties": {
"navigation": {
"type": "object",
"properties": {
"heading": {
"$ref": "#/definitions/heading"
},
"course": {
"$ref": "#/definitions/course"
},
"coords": {
"$ref": "#/definitions/coords"
},
"velocity": {
"$ref": "#/definitions/velocity"
}
}
}
}
}
{
$id : "http://aotds/battle/ship",
title : "Ship",
definitions : {
course : {
type : "array",
description : "projected movement for new turn",
items : {
type : "object",
properties : {
heading : {
$ref : "#/definitions/heading"
},
coords : {
$ref : "#/definitions/coords"
}
}
}
},
heading : {
type : "number",
description : "forward angle of the object",
minimum : 0,
maximum : 12
},
coords : {
type : "array",
items : {
type: "number"
},
minItems : 2,
maxItems : 2
},
velocity : {
description : "speed of the object",
type : "number",
minimum : 0
}
},
type : "object",
properties : {
navigation : {
type : "object",
properties : {
heading : {
$ref : "#/definitions/heading"
},
course : {
$ref : "#/definitions/course"
},
coords : {
$ref : "#/definitions/coords"
},
velocity : {
$ref : "#/definitions/velocity"
}
}
}
}
}
{
$id : "http://aotds/battle/ship",
title : "Ship",
definitions : {
course : {
type : "array",
description : "projected movement for new turn",
items : {
type : "object",
properties : {
heading : {
$ref : "#/definitions/heading"
},
coords : {
$ref : "#/definitions/coords"
}
}
}
},
heading : {
type : "number",
description : "forward angle of the object",
minimum : 0,
maximum : 12
},
coords : {
type : "array",
items : {
type: "number"
},
minItems : 2,
maxItems : 2
},
velocity : {
description : "speed of the object",
type : "number",
minimum : 0
}
},
type : "object",
properties : {
navigation : {
type : "object",
properties : {
heading : {
$ref : "#/definitions/heading"
},
course : {
$ref : "#/definitions/course"
},
coords : {
$ref : "#/definitions/coords"
},
velocity : {
$ref : "#/definitions/velocity"
}
}
}
}
}
}
Granted, not a huge difference. Still, the removal of all those pesky quotes does clear up the air.
A lot
Now, if we really want to cut on the noise, we could go full-tilt and work with YAML.
{
"$id" : "http://aotds/battle/ship",
"title" : "Ship",
"definitions" : {
"course" : {
"type" : "array",
"description" : "projected movement for new turn",
"items" : {
"type" : "object",
"properties" : {
"heading" : {
"$ref" : "#/definitions/heading"
},
"coords" : {
"$ref" : "#/definitions/coords"
}
}
}
},
"heading" : {
"type" : "number",
"description" : "forward angle of the object",
"minimum" : 0,
"maximum" : 12
},
"coords" : {
"type" : "array",
"items" : {
"type": "number"
},
"minItems" : 2,
"maxItems" : 2
},
"velocity" : {
"description" : "speed of the object",
"type" : "number",
"minimum" : 0
}
},
"type" : "object",
"properties" : {
"navigation" : {
"type" : "object",
"properties" : {
"heading" : {
"$ref" : "#/definitions/heading"
},
"course" : {
"$ref" : "#/definitions/course"
},
"coords" : {
"$ref" : "#/definitions/coords"
},
"velocity" : {
"$ref" : "#/definitions/velocity"
}
}
}
}
}
---
$id: http://aotds/battle/ship
title: Ship
definitions:
course:
type: array
description: projected movement for new turn
items:
type: object
properties:
heading:
$ref: '#/definitions/heading'
coords:
$ref: '#/definitions/coords'
heading:
type: number
description: forward angle of the object
minimum: 0
maximum: 12
coords:
type: array
items:
type: number
minItems: 2
maxItems: 2
velocity:
description: speed of the object
type: number
minimum: 0
type: object
properties:
navigation:
type: object
properties:
heading:
$ref: '#/definitions/heading'
course:
$ref: '#/definitions/course'
coords:
$ref: '#/definitions/coords'
velocity:
$ref: '#/definitions/velocity'
---
$id: http://aotds/battle/ship
title: Ship
definitions:
course:
type: array
description: projected movement for new turn
items:
type: object
properties:
heading:
$ref: '#/definitions/heading'
coords:
$ref: '#/definitions/coords'
heading:
type: number
description: forward angle of the object
minimum: 0
maximum: 12
coords:
type: array
items:
type: number
minItems: 2
maxItems: 2
velocity:
description: speed of the object
type: number
minimum: 0
type: object
properties:
navigation:
type: object
properties:
heading:
$ref: '#/definitions/heading'
course:
$ref: '#/definitions/course'
coords:
$ref: '#/definitions/coords'
velocity:
$ref: '#/definitions/velocity'
Love or hate whitespace-based formats, be honest for a minute here: ain’t that much better to read?
Sidestep the problem to the right…
Using a different static format already helps, but maybe we could escalate things and go dynamic. Like, how about leveraging the powers of JavaScript to help us with the repetitive and boring stuff?
{
"$id" : "http://aotds/battle/ship",
"title" : "Ship",
"definitions" : {
"course" : {
"type" : "array",
"description" : "projected movement for new turn",
"items" : {
"type" : "object",
"properties" : {
"heading" : {
"$ref" : "#/definitions/heading"
},
"coords" : {
"$ref" : "#/definitions/coords"
}
}
}
},
"heading" : {
"type" : "number",
"description" : "forward angle of the object",
"minimum" : 0,
"maximum" : 12
},
"coords" : {
"type" : "array",
"items" : {
"type": "number"
},
"minItems" : 2,
"maxItems" : 2
},
"velocity" : {
"description" : "speed of the object",
"type" : "number",
"minimum" : 0
}
},
"type" : "object",
"properties" : {
"navigation" : {
"type" : "object",
"properties" : {
"heading" : {
"$ref" : "#/definitions/heading"
},
"course" : {
"$ref" : "#/definitions/course"
},
"coords" : {
"$ref" : "#/definitions/coords"
},
"velocity" : {
"$ref" : "#/definitions/velocity"
}
}
}
}
}
export default {
$id : "http://aotds/battle/ship",
title : "Ship",
definitions : {
course : {
type : "array",
description : "projected movement for new turn",
items : {
properties : {
heading : {
$ref : "#/definitions/heading"
},
coords : {
$ref : "#/definitions/coords"
}
},
type : "object"
}
},
heading : {
type : "number",
description : "forward angle of the object",
minimum : 0,
maximum : 12
},
coords : {
type : "array",
items : {
type: "number"
},
minItems : 2,
maxItems : 2
},
velocity : {
description : "speed of the object",
type : "number",
minimum : 0
}
},
type : "object",
properties : {
navigation : {
type : "object",
properties : {
heading : {
$ref : "#/definitions/heading"
},
course : {
$ref : "#/definitions/course"
},
coords : {
$ref : "#/definitions/coords"
},
velocity : {
$ref : "#/definitions/velocity"
}
},
}
}
};
export default {
$id : "http://aotds/battle/ship",
title : "Ship",
definitions : {
course : {
type : "array",
description : "projected movement for new turn",
items : {
properties : {
heading : {
$ref : "#/definitions/heading"
},
coords : {
$ref : "#/definitions/coords"
}
},
type : "object"
}
},
heading : {
type : "number",
description : "forward angle of the object",
minimum : 0,
maximum : 12
},
coords : {
type : "array",
items : {
type: "number"
},
minItems : 2,
maxItems : 2
},
velocity : {
description : "speed of the object",
type : "number",
minimum : 0
}
},
type : "object",
properties : {
navigation : {
type : "object",
properties : {
heading : {
$ref : "#/definitions/heading"
},
course : {
$ref : "#/definitions/course"
},
coords : {
$ref : "#/definitions/coords"
},
velocity : {
$ref : "#/definitions/velocity"
}
},
}
}
};
I know what you are thinking, that doesn’t give us nothing more
json5
already did, but hang on, just hang on…
Divide and conquer
One thing that JavaScript makes possible is splintering the whole schema into smaller, easier to digest segments. For a start, let’s segregate the definitions from the main object schema. That won’t make the code any shorter, but we’ll now have two pieces that we can work on independently. If things grow out of control, we could even go ahead and move them into different files.
export default {
$id : "http://aotds/battle/ship",
title : "Ship",
definitions : {
course : {
type : "array",
description : "projected movement for new turn",
items : {
properties : {
heading : {
$ref : "#/definitions/heading"
},
coords : {
$ref : "#/definitions/coords"
}
},
type : "object"
}
},
heading : {
type : "number",
description : "forward angle of the object",
minimum : 0,
maximum : 12
},
coords : {
type : "array",
items : {
type: "number"
},
minItems : 2,
maxItems : 2
},
velocity : {
description : "speed of the object",
type : "number",
minimum : 0
}
},
type : "object",
properties : {
navigation : {
type : "object",
properties : {
heading : {
$ref : "#/definitions/heading"
},
course : {
$ref : "#/definitions/course"
},
coords : {
$ref : "#/definitions/coords"
},
velocity : {
$ref : "#/definitions/velocity"
}
},
}
}
};
const definitions = {
course : {
type : "array",
description : "projected movement for new turn",
items : {
properties : {
heading : {
$ref : "#/definitions/heading"
},
coords : {
$ref : "#/definitions/coords"
}
},
type : "object"
}
},
heading : {
type : "number",
description : "forward angle of the object",
minimum : 0,
maximum : 12
},
coords : {
type : "array",
items : {
type: "number"
},
minItems : 2,
maxItems : 2
},
velocity : {
description : "speed of the object",
type : "number",
minimum : 0
}
};
export default {
definitions,
$id : "http://aotds/battle/ship",
title : "Ship",
type : "object",
properties : {
navigation : {
type : "object",
properties : {
heading : {
$ref : "#/definitions/heading"
},
course : {
$ref : "#/definitions/course"
},
coords : {
$ref : "#/definitions/coords"
},
velocity : {
$ref : "#/definitions/velocity"
}
},
}
}
}
const definitions = {
course : {
type : "array",
description : "projected movement for new turn",
items : {
properties : {
heading : {
$ref : "#/definitions/heading"
},
coords : {
$ref : "#/definitions/coords"
}
},
type : "object"
}
},
heading : {
type : "number",
description : "forward angle of the object",
minimum : 0,
maximum : 12
},
coords : {
type : "array",
items : {
type: "number"
},
minItems : 2,
maxItems : 2
},
velocity : {
description : "speed of the object",
type : "number",
minimum : 0
}
};
export default {
definitions,
$id : "http://aotds/battle/ship",
title : "Ship",
type : "object",
properties : {
navigation : {
type : "object",
properties : {
heading : {
$ref : "#/definitions/heading"
},
course : {
$ref : "#/definitions/course"
},
coords : {
$ref : "#/definitions/coords"
},
velocity : {
$ref : "#/definitions/velocity"
}
},
}
}
}
Pseudo-types
Next in line: all those $ref
are rather verbose. Why not define
succinct, easier to read aliases, and use them as pseudo-types?
const definitions = {
course : {
type : "array",
description : "projected movement for new turn",
items : {
properties : {
heading : {
$ref : "#/definitions/heading"
},
coords : {
$ref : "#/definitions/coords"
}
},
type : "object"
}
},
heading : {
type : "number",
description : "forward angle of the object",
minimum : 0,
maximum : 12
},
coords : {
type : "array",
items : {
type: "number"
},
minItems : 2,
maxItems : 2
},
velocity : {
description : "speed of the object",
type : "number",
minimum : 0
}
};
export default {
definitions,
$id : "http://aotds/battle/ship",
title : "Ship",
type : "object",
properties : {
navigation : {
type : "object",
properties : {
heading : {
$ref : "#/definitions/heading"
},
course : {
$ref : "#/definitions/course"
},
coords : {
$ref : "#/definitions/coords"
},
velocity : {
$ref : "#/definitions/velocity"
}
},
}
}
}
const defLink = name => ({ $ref: '#/definitions/' + name });
const heading = defLink('heading');
const coords = defLink('coords');
const course = defLink('course');
const velocity = defLink('velocity');
const definitions = {
course : {
type : "array",
description : "projected movement for new turn",
items : {
type : "object",
properties : {
heading,
coords
}
}
},
heading : {
type : "number",
description : "forward angle of the object",
minimum : 0,
maximum : 12
},
coords : {
type : "array",
items : {
type: "number"
},
minItems : 2,
maxItems : 2
},
velocity : {
description : "speed of the object",
type : "number",
minimum : 0
}
};
export default {
definitions,
$id : "http://aotds/battle/ship",
title : "Ship",
type : "object",
properties : {
navigation : {
type : "object",
properties : {
heading,
coords,
course,
velocity
},
}
}
}
const defLink = name => ({ $ref: '#/definitions/' + name });
const heading = defLink('heading');
const coords = defLink('coords');
const course = defLink('course');
const velocity = defLink('velocity');
const definitions = {
course : {
type : "array",
description : "projected movement for new turn",
items : {
type : "object",
properties : {
heading,
coords
}
}
},
heading : {
type : "number",
description : "forward angle of the object",
minimum : 0,
maximum : 12
},
coords : {
type : "array",
items : {
type: "number"
},
minItems : 2,
maxItems : 2
},
velocity : {
description : "speed of the object",
type : "number",
minimum : 0
}
};
export default {
definitions,
$id : "http://aotds/battle/ship",
title : "Ship",
type : "object",
properties : {
navigation : {
type : "object",
properties : {
heading,
coords,
course,
velocity
},
}
}
}
Pseudo-types, swankier
We took a first step into cutting the holistic schema into logical parts, but the definitions are still set in a block. If we think about it, there is no good reason for them to be blobbed together thus. Also, wouldn’t it be nice to also herd the logic such that defining the types and their aliases would be a one-step operation rather than a two-step dance?
Cue in my own humble npm module json-schema-shorthand which offers a few helper functions that help smooth the abrasive bits of the schema.
First of the bunch: add_definition
, which appends a definition to a
main collection while returning its alias.
const defLink = name => ({ $ref: '#/definitions/' + name });
const heading = defLink('heading');
const coords = defLink('coords');
const course = defLink('course');
const velocity = defLink('velocity');
const definitions = {
course : {
type : "array",
description : "projected movement for new turn",
items : {
type : "object",
properties : {
heading,
coords
}
}
},
heading : {
type : "number",
description : "forward angle of the object",
minimum : 0,
maximum : 12
},
coords : {
type : "array",
items : {
type: "number"
},
minItems : 2,
maxItems : 2
},
velocity : {
description : "speed of the object",
type : "number",
minimum : 0
}
};
export default {
definitions,
$id : "http://aotds/battle/ship",
title : "Ship",
type : "object",
properties : {
navigation : {
type : "object",
properties : {
heading,
coords,
course,
velocity
},
}
}
}
import { add_definition } from 'json-schema-shorthand';
let definitions = {};
const add_def = add_definition.bind(definitions);
const course = add_def('course', {
type : "array",
description : "projected movement for new turn",
items : {
type : "object",
properties : { heading, coords }
}
});
const heading = add_def( 'heading', {
type : "number",
description : "forward angle of the object",
minimum : 0,
maximum : 12
});
const coords = add_def('coords', {
type : "array",
items : {
type: "number"
},
minItems : 2,
maxItems : 2
});
const velocity = add_def('velocity', {
description : "speed of the object",
type : "number",
minimum : 0
});
export default {
definitions,
$id : "http://aotds/battle/ship",
title : "Ship",
type : "object",
properties : {
navigation : {
type : "object",
properties : {
heading,
coords,
course,
velocity
},
}
}
}
import { add_definition } from 'json-schema-shorthand';
let definitions = {};
const add_def = add_definition.bind(definitions);
const course = add_def('course', {
type : "array",
description : "projected movement for new turn",
items : {
type : "object",
properties : { heading, coords }
}
});
const heading = add_def( 'heading', {
type : "number",
description : "forward angle of the object",
minimum : 0,
maximum : 12
});
const coords = add_def('coords', {
type : "array",
items : {
type: "number"
},
minItems : 2,
maxItems : 2
});
const velocity = add_def('velocity', {
description : "speed of the object",
type : "number",
minimum : 0
});
export default {
definitions,
$id : "http://aotds/battle/ship",
title : "Ship",
type : "object",
properties : {
navigation : {
type : "object",
properties : {
heading,
coords,
course,
velocity
},
}
}
}
Tired of typing typical type
terms?
The { type: ... }
declarations are also something that appears over
and over again, and they are kind of long-winded. Let’s shrink that
too with helper functions.
import { add_definition } from 'json-schema-shorthand';
let definitions = {};
const add_def = add_definition.bind(definitions);
const course = add_def('course', {
type : "array",
description : "projected movement for new turn",
items : {
type : "object",
properties : { heading, coords }
}
});
const heading = add_def( 'heading', {
type : "number",
description : "forward angle of the object",
minimum : 0,
maximum : 12
});
const coords = add_def('coords', {
type : "array",
items : {
type: "number"
},
minItems : 2,
maxItems : 2
});
const velocity = add_def('velocity', {
description : "speed of the object",
type : "number",
minimum : 0
});
export default {
definitions,
$id : "http://aotds/battle/ship",
title : "Ship",
type : "object",
properties : {
navigation : {
type : "object",
properties : {
heading,
coords,
course,
velocity
},
}
}
}
import { object, array, number, add_definition } from 'json-schema-shorthand';
let definitions = {};
const add_def = add_definition.bind(definitions);
const course = add_def( 'course', array(
object({ heading, coords }),
{ description : "projected movement for new turn" }
));
const heading = add_def( 'heading', number({
description : "forward angle of the object",
minimum : 0,
maximum : 12
}));
const coords = add_def( 'coords', array( 'number', {
minItems : 2,
maxItems : 2
}));
const velocity = add_def('velocity', number({
description : "speed of the object",
minimum : 0
}));
export default object(
{ navigation : object({ heading, coords, course, velocity }) },
{
definitions,
$id : "http://aotds/battle/ship",
title : "Ship"
}
);
import { object, array, number, add_definition } from 'json-schema-shorthand';
let definitions = {};
const add_def = add_definition.bind(definitions);
const course = add_def( 'course', array(
object({ heading, coords }),
{ description : "projected movement for new turn" }
));
const heading = add_def( 'heading', number({
description : "forward angle of the object",
minimum : 0,
maximum : 12
}));
const coords = add_def( 'coords', array( 'number', {
minItems : 2,
maxItems : 2
}));
const velocity = add_def('velocity', number({
description : "speed of the object",
minimum : 0
}));
export default object(
{ navigation : object({ heading, coords, course, velocity }) },
{
definitions,
$id : "http://aotds/battle/ship",
title : "Ship"
}
);
A few last tweaks
As a last touch, we can also use a few of the shorthand forms and
extra-spec keywords that json-schema-shorthand
provides — like
range
that combines min and max values, the detection and expansion
of description strings… Y’know. Sprinkly magic.
import { object, array, number, add_definition } from 'json-schema-shorthand';
let definitions = {};
const add_def = add_definition.bind(definitions);
const course = add_def( 'course', array(
object({ heading, coords }),
{ description : "projected movement for new turn" }
));
const heading = add_def( 'heading', number({
description : "forward angle of the object",
minimum : 0,
maximum : 12
}));
const coords = add_def( 'coords', array( 'number', {
minItems : 2,
maxItems : 2
}));
const velocity = add_def('velocity', number({
description : "speed of the object",
minimum : 0
}));
export default object(
{ navigation : object({ heading, coords, course, velocity }) },
{
definitions,
$id : "http://aotds/battle/ship",
title : "Ship"
}
);
import { object, array, number, add_definition } from 'json-schema-shorthand';
let definitions = {};
const add_def = add_definition.bind(definitions);
const course = add_def( 'course',
"projected movement for new turn"
array( object({ heading, coords }) ),
);
const heading = add_def( 'heading',
"forward angle of the object",
number({ range: [ 0, 12 ] }),
);
const coords = add_def( 'coords',
array( 'number', { nbrItems: 2, })
);
const velocity = add_def( 'velocity',
"speed of the object",
number({ minimum : 0 }),
);
export default object(
{ navigation : object({ heading, coords, course, velocity }) },
{
definitions,
$id : "http://aotds/battle/ship",
title : "Ship"
}
);
import { object, array, number, add_definition } from 'json-schema-shorthand';
let definitions = {};
const add_def = add_definition.bind(definitions);
const course = add_def( 'course',
"projected movement for new turn"
array( object({ heading, coords }) ),
);
const heading = add_def( 'heading',
"forward angle of the object",
number({ range: [ 0, 12 ] }),
);
const coords = add_def( 'coords',
array( 'number', { nbrItems: 2, })
);
const velocity = add_def( 'velocity',
"speed of the object",
number({ minimum : 0 }),
);
export default object(
{ navigation : object({ heading, coords, course, velocity }) },
{
definitions,
$id : "http://aotds/battle/ship",
title : "Ship"
}
);
Final form most beautiful
Now we can take a step back and compare the original JSON document with its final JavaScript incarnation.
{
"$id" : "http://aotds/battle/ship",
"title" : "Ship",
"definitions" : {
"course" : {
"type" : "array",
"description" : "projected movement for new turn",
"items" : {
"type" : "object",
"properties" : {
"heading" : {
"$ref" : "#/definitions/heading"
},
"coords" : {
"$ref" : "#/definitions/coords"
}
}
}
},
"heading" : {
"type" : "number",
"description" : "forward angle of the object",
"minimum" : 0,
"maximum" : 12
},
"coords" : {
"type" : "array",
"items" : {
"type": "number"
},
"minItems" : 2,
"maxItems" : 2
},
"velocity" : {
"description" : "speed of the object",
"type" : "number",
"minimum" : 0
}
},
"type" : "object",
"properties" : {
"navigation" : {
"type" : "object",
"properties" : {
"heading" : {
"$ref" : "#/definitions/heading"
},
"course" : {
"$ref" : "#/definitions/course"
},
"coords" : {
"$ref" : "#/definitions/coords"
},
"velocity" : {
"$ref" : "#/definitions/velocity"
}
}
}
}
}
import { object, array, number, add_definition } from 'json-schema-shorthand';
let definitions = {};
const add_def = add_definition.bind(definitions);
const course = add_def( 'course',
"projected movement for new turn"
array( object({ heading, coords }) ),
);
const heading = add_def( 'heading',
"forward angle of the object",
number({ range: [ 0, 12 ] }),
);
const coords = add_def( 'coords',
array( 'number', { nbrItems: 2, })
);
const velocity = add_def( 'velocity',
"speed of the object",
number({ minimum : 0 }),
);
export default object(
{ navigation : object({ heading, coords, course, velocity }) },
{
definitions,
$id : "http://aotds/battle/ship",
title : "Ship"
}
);
import { object, array, number, add_definition } from 'json-schema-shorthand';
let definitions = {};
const add_def = add_definition.bind(definitions);
const course = add_def( 'course',
"projected movement for new turn"
array( object({ heading, coords }) ),
);
const heading = add_def( 'heading',
"forward angle of the object",
number({ range: [ 0, 12 ] }),
);
const coords = add_def( 'coords',
array( 'number', { nbrItems: 2, })
);
const velocity = add_def( 'velocity',
"speed of the object",
number({ minimum : 0 }),
);
export default object(
{ navigation : object({ heading, coords, course, velocity }) },
{
definitions,
$id : "http://aotds/battle/ship",
title : "Ship"
}
);
The JavaScript version is smaller, which is nice. It also broke the monolithic document into logical fragments, which is better. But, most importantly to my eyes, it shifted the emphasis of the written code from the “how” to the “what”, ensuring that desirable actions like adding descriptions to definitions are easy and up-front-and-center.
Enjoy!
Tags: technology javascript json-schema