JSON Schema to Validate Objects for Downstream consumers

JSON Schema icon
  • required and optional fields
  • nested objects
  • prebuilt validations
  • custom validations
[
{
"firstName": "john",
"lastName": "smith",
"birthDay": "1980-01-01",
"address": {
"streetNumber": "10",
"streetName": "Washington",
"city": "Boston",
"state": "MA",
"zip": "02110"
}
},
{
"firstName": "john",
"lastName": "smith",
"address": {
"streetNumber": "10",
"streetName": "Washington",
"apartmentNumber": "a",
"city": "Boston",
"state": "MA",
"zip": "02110"
}
},
{
"firstName": "john",
"lastName": "smith",
"address": {
"streetNumber": "10",
"streetName": "Washington",
"apartmentNumber": "a",
"city": "Boston",
"state": "MA"
}
}
]
{
"definitions": {},
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "http://example.com/root.json",
"type": "object",
"title": "The Person Schema",
"required": [
"firstName",
"lastName",
"address"
],
"properties": {
"firstName": {
"$id": "#/properties/firstName",
"type": "string",
"title": "The firstName Schema"
},
"lastName": {
"$id": "#/properties/lastName",
"type": "string",
"title": "The lastName Schema"
},
"birthDay": {
"$id": "#/properties/birthDay",
"type": "string",
"format": "date",
"title": "The birthDay Schema"
},
"address": {
"$id": "#/properties/address",
"type": "object",
"title": "The address Schema",
"required": [
"streetNumber",
"streetName",
"city",
"state",
"zip"
],
"properties": {
"streetNumber": {
"$id": "#/properties/address/properties/streetNumber",
"type": "string",
"title": "The address/streetNumber Schema"
},
"streetName": {
"$id": "#/properties/address/properties/streetName",
"type": "string",
"title": "The address/streetName Schema"
},
"apartmentNumber": {
"$id": "#/properties/address/properties/apartmentNumber",
"type": "string",
"title": "The address/apartmentNumber Schema"
},
"city": {
"$id": "#/properties/address/properties/city",
"type": "string",
"title": "The address/city Schema"
},
"state": {
"$id": "#/properties/address/properties/state",
"type": "string",
"title": "The address/state Schema"
},
"zip": {
"$id": "#/properties/address/properties/zipCode",
"type": "string",
"title": "The address/zipCode Schema",
"pattern": "^[0-9]{5}$"
}
}
}
}
}
/*****************************************************************
* Validate some widgets and handle the results
* in this case handle success with console log
* handle failures with console.error
******************************************************************/
const personSchema = require('./PersonSchema_v1.0.json')
, persons = require('./Person.json') ;

const personsValidator = getListValidator(personSchema)

const results = personsValidator(persons)
// => [Either a' a]

const pretty = x => JSON.stringify(x, null, 4)
const handleSuccess = x => console.log("valid: ", pretty(x))
const handleFailure = x => console.error("invalid: ", pretty(x))

const handleValidation = bimap(handleFailure)(handleSuccess)
const handleValidations = map(handleValidation)

handleValidations(results)
const Ajv = require('ajv')
, { Left, Right, pipe, map, bimap } = require('sanctuary') ;

// :: (a -> boolean) -> a -> Either a' a
const validateToEither = validator => x => {
const validatorResult = validator(x)
if(validatorResult === true){
return Right(x)
} else {
return Left({"input": x, "error": validator.errors})
}
}

// :: JsonSchema -> (a -> boolean)
const getBaseValidator = schema => {
const ajv = new Ajv({}) ;
return ajv.compile(schema);
}

// :: JsonSchema -> a -> Either a' a
const getObjectValidator = pipe([
getBaseValidator,
validateToEither
])

// :: JsonSchema -> [{}] -> [Either a' a]
const getListValidator = pipe([
getObjectValidator,
map
])
const getObjectValidator = jsonSchema => {
const objectValidator = getBaseValidator(jsonSchema)
const objectValidatorToEither =
validateToEither(objectValidator)
return objectValidatorToEither
}
const getListValidator = jsonSchema => xs => {
const objectValidator = getObjectValidator(jsonSchema)
const results = map(objectValidator)(xs)
return results
}

--

--

--

A 25 year software industry veteran with a passion for functional programming, architecture, mentoring / team development, xp/agile and doing the right thing.

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Day 2: Indexing, Aggregating, Mapreduce

images/mongo-mapreduce.png

Not Another To Do App

Create a Scroll to Top Arrow Using React Hooks

Tip #8 [initial value in RxJS stream]

JavaScript callback functions

A Guide To Prototype-Based Class Inheritance In JavaScript

Essentials of Javascript :) (Part1)

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Todd Brown

Todd Brown

A 25 year software industry veteran with a passion for functional programming, architecture, mentoring / team development, xp/agile and doing the right thing.

More from Medium

Attacker adds evasive technique to their ongoing attacks on NPM

Microsites Architecture using Nrwl Nx

Implementing a custom collection type in clojure — the leftist heap, a persistent priority queue

A teacup placed on a pile of books

Using Tapir in a Play Framework application