Slide 1

Slide 1 text

JSON Schema Core Concepts, Common Pitfalls, and Debugging Ben Hutton @relequestual on the internet JSON Schema core opencollective.com/json-schema ☕ ko-fi.com/relequestual ASC 2019 - API Specification Conference 2019 These slides have interactive code which does not display fully in PDF. Please see https://stoic-agnesi-d0ac4a.netlify.com for the full version. All code used and created for this deck is avilable on github.

Slide 2

Slide 2 text

Validation saves lives

Slide 3

Slide 3 text

Validation (probably?) saves lives

Slide 4

Slide 4 text

Validation changes lives Validation (probably?) saves lives

Slide 5

Slide 5 text

Story

Slide 6

Slide 6 text

Matchmaker Exchange API Siloed databases of patient data (including DNA variants) Rare and undiagnosed cases (> 10 per country rare) Complex logal and ethical situation Discover and exchange pateint data

Slide 7

Slide 7 text

Matchmaker Exchange API Each database holds slightly different data Representation of a patient needs to be uniform Write specification using words and examples Release v1.0! Sounds easy, right?

Slide 8

Slide 8 text

Nope

Slide 9

Slide 9 text

Building a schema

Slide 10

Slide 10 text

Requirements for queries Root object must have a patient patient must have either properties genomicFeatures or phenotypicFeatures or both

Slide 11

Slide 11 text

{ "$schema": "http://json-schema.org/draft-07/schema", "title": "MatchMakerExchange format for queries", "patient": { "$comment": "`patient`: `genomicFeatures` or `phenotypicFeatures` or both", "anyOf": [ "genomicFeatures", "phenotypicFeatures" ] } } This is an unknown keyword. Unknown keywords are ignored.

Slide 12

Slide 12 text

What's a "schema" anyway?

Slide 13

Slide 13 text

JSON Schema A vocabulary that allows you to annotate and validate JSON documents. An IETF "personal draft" document specification JSON! Must be a boolean or an object

Slide 14

Slide 14 text

JSON Schema This talk covers draft-7. Not draft-4, not draft-8 draft-8 "draft 2019-09 " is out, but implementations need to catch up draft-7 is the latest well supported version

Slide 15

Slide 15 text

Key Concepts Schema “keywords” : Object properties that are applied to the instance Instance : The JSON document being validated or associated with a Schema Root Schema : A Schema that is the whole JSON document Subschema : A Schema as a value of an object or array Keywords fall under several behavior categories which include: Assertions : produce a boolean result when applied to an instance Annotations : attach information to an instance for application use

Slide 16

Slide 16 text

"properties" keyword properties is an object The values of the object are applied to the instance's matching key's value

Slide 17

Slide 17 text

{ "$schema": "http://json-schema.org/draft-07/schema", "title": "MatchMakerExchange format for queries", "properties": { "patient": { "$comment": "`patient`: `genomicFeatures` or `phenotypicFeatures` or both", "anyOf": [ { "properties": { "phenotypicFeatures": true } }, { "properties": { "genomicFeatures": true } } ] } } } Remember, the values of a `properties` object must be Schemas. A Boolean is a valid Schema.

Slide 18

Slide 18 text

A Boolean can be a schema?

Slide 19

Slide 19 text

What's a Schema again? (pt 2)

Slide 20

Slide 20 text

An Object or a Boolean Always passes validation {} An empty object true a `true` boolean value Always fails validation { "not": {} } `not` keyword: inverts the assertion false a `false` boolean value

Slide 21

Slide 21 text

Does it work? { "$schema": "http://json-schema.org/draft-07/schema", "title": "MatchMakerExchange format for queries", "properties": { "patient": { "anyOf": [ { "properties": { "phenotypicFeatures": { "type": [ "array" ] } } }, { "properties": { "genomicFeatures": { "type": [ "array" ] } } } ] } } { "patient": { "phenotypicFeatures": "long nose" } } But didn't I define that should be an array?

Slide 22

Slide 22 text

anyOf what?

Slide 23

Slide 23 text

(More) Key Concepts Applicator keywords: Determine how subschemas are applied to an instance location. Include, but not limited to; oneOf , allOf , and anyOf

Slide 24

Slide 24 text

*Of applicators The *Of keywords values must be an array of Schemas. The result of applying a schema to an instance includes an assertion of validity. The resulting assertions are modified or combined to produce a final result. For example, oneOf requires that ONLY one of the Schemas in the array is valid.

Slide 25

Slide 25 text

Why doesn't it work? { "$schema": "http://json-schema.org/draft-07/schema", "title": "MatchMakerExchange format for queries", "properties": { "patient": { "anyOf": [ { "properties": { "phenotypicFeatures": { "type": [ "array" ] } }, "additionalProperties": false }, { "properties": { "genomicFeatures": { "type": [ "array" ] } }, "additionalProperties": false } ] } } } { "patient": { "phenotypicFeatures": "long nose" } } ... ❌ YES! Validation fails, as expected

Slide 26

Slide 26 text

We fixed it! Almost

Slide 27

Slide 27 text

{ "$schema": "http://json-schema.org/draft-07/schema", "title": "MatchMakerExchange format for queries", "properties": { "patient": { "anyOf": [ { "required": [ "phenotypicFeatures" ], "properties": { "phenotypicFeatures": { "type": [ "array" ] } }, "additionalProperties": false }, { "required": [ "genomicFeatures" ], "properties": { "genomicFeatures": { "type": [ "array" ] } }, "additionalProperties": false } ] } { "patient": { } } ❌ Validation now fails as expected!

Slide 28

Slide 28 text

What did we discover? Some keywords have values that are a JSON Schema (a subschema) *Of keywords are applicators that take an array of subschemas You can set the value of a subschema to a boolean false to check the validation path is as you expect The values of a properties object are subschemas which are applied to an objects values for matching keys

Slide 29

Slide 29 text

Spec change! That doesn't happen, does it?

Slide 30

Slide 30 text

Spec change Tidy the subschemas into definitions Refactor the schema patient now needs to incldue some additional fields. A definition is provided.

Slide 31

Slide 31 text

Let's do this

Slide 32

Slide 32 text

{ "$schema": "http://json-schema.org/draft-07/schema", "title": "MatchMakerExchange format for queries", "definitions": { "phenotypicFeatures": { "type": [ "array" ] }, "genomicFeatures": { "type": [ "array" ] }, "geneticsPatient": { "properties": { "phenotypicFeatures": { "$ref": "#/definitions/phenotypicFeatures" }, "genomicFeatures": { "$ref": "#/definitions/genomicFeatures" } }, "additionalProperties": false, "anyOf": [ { "required": [ "phenotypicFeatures" ] }, { "required": [ "genomicFeatures" ] } ] }, "regularPatient": { "type": "object", "required": [ "name" ], "properties": { "name": { "type": [ "string" ] } } } }, "properties": { "patient": { "allOf": [ { "$ref": "#/definitions/regularPatient" }, { "$ref": "#/definitions/geneticsPatient" } ] Validation always fails... how do we fix this?

Slide 33

Slide 33 text

Validation Error: should NOT have additional properties. additionalProperties at "#/additionalProperties" Instance location: "/patient" { "patient": { "phenotypicFeatures": ["long nose"], "name": "bob" } } Does it work? ❌ Validation always fails... how do we fix this?

Slide 34

Slide 34 text

required : [ "genomicFeatures" ] } ] }, "regularPatient": { "type": "object", "required": [ "name" ], "properties": { "name": { "type": [ "string" ] } } } }, "properties": { "patient": { ... and from `geneticsPatient`, but no others.

Slide 35

Slide 35 text

additionalProperties What specifically does additionalProperties DO? additionalProperties takes a schema as it's value. Often this is boolean false . The schema is only applied to the instance object's values, where the keys have not already been evaluated as a result of properties or patternProperties within the same schema object. additionalProperties cannot "see through" applicator keywords (such as allOf and $ref references) Validation with "additionalProperties" applies only to the child values of instance names that do not match any names in "properties", and do not match any regular expression in "patternProperties".

Slide 36

Slide 36 text

type : [ "string" ] } } } }, "properties": { "patient": { "additionalProperties": false, "properties": { "name": true, "phenotypicFeatures": true, "genomicFeatures": true }, "allOf": [ { "$ref": "#/definitions/regularPatient" }, { "$ref": "#/definitions/geneticsPatient" } The only solution is to add `properties`, but we don't want to define any constraints on their value in this subschema

Slide 37

Slide 37 text

We have our schema Let's recap how it works

Slide 38

Slide 38 text

{ "$schema": "http://json-schema.org/draft-07/schema", "title": "MatchMakerExchange format for queries", "definitions": { "phenotypicFeatures": { "type": [ "array" ] }, "genomicFeatures": { "type": [ "array" ] }, "geneticsPatient": { "properties": { "phenotypicFeatures": { "$ref": "#/definitions/phenotypicFeatures" }, "genomicFeatures": { "$ref": "#/definitions/genomicFeatures" } }, "anyOf": [ { "required": [ "phenotypicFeatures" ] }, { "required": [ { "patient": { "phenotypicFeatures": ["long nose"], "name": "bob" } } `phenotypicFeatures` and `genomicFeatures` are defined in `definitions` and are referenced by the individual properties of `geneticsPatient`

Slide 39

Slide 39 text

allOf > $ref schemas and additionalProperties: false Being able to combine schema objects without additional properties is a VERY common pain point. We recognised this and fixed it in draft-8, but that's a whole other talk.

Slide 40

Slide 40 text

What else did we discover? Schemas may be boolean values, even when as a value of the properties object Constructing and merging complex schemas may require some duplication additionalProperties CANNOT "See Through" applicator keywords like *Of or $ref

Slide 41

Slide 41 text

Thank you all Previous team Current team Contributors and community Implementation developers all of you

Slide 42

Slide 42 text

JSON Schema Core Concepts, Common Pitfalls, and Debugging Thank you sponsors! Ben Hutton @relequestual on the internet JSON Schema core opencollective.com/json-schema ☕ ko-fi.com/relequestual ASC 2019 - API Specification Conference 2019