Sei sulla pagina 1di 307

JSON and JSON-Schema

for XML Developers


|4|

Table of Contents

Comparison of XML and JSON


JSON
JSON Schema
|6|

XML is a data format

XML is a way of structuring data XML is a data format.


|7|

JSON is a data format

JSON is a way of structuring data JSON is a data format.


|8|

Example of XML-formatted data

The below XML document contains data about a book: its title,
authors, date of publication, and publisher.

<Book>
<Title>Parsing Techniques</Title>
<Authors>
<Author>Kathy Sierra</Author>
<Author>Ceriel J.H. Jacobs</Author>
</Authors>
<Date>2007</Date>
<Publisher>Springer</Publisher>
</Book>
|9|

Same data, JSON-formatted

{
"Book":
{
"Title": "Parsing Techniques",
"Authors": [ "Kathy Sierra", "Ceriel J.H. Jacobs" ],
"Date": "2007",
"Publisher": "Springer"
}
}
| 7|

XML and JSON, side-by-side

{
<Book> "Book":
<Title>Parsing Techniques</Title> {
<Authors> "Title": "Parsing Techniques",
<Author>Kathy Sierra</Author> "Authors": [ "Kathy Sierra", "Ceriel J.H. Jacobs" ],
<Author>Ceriel J.H. Jacobs</Author> "Date": "2007",
</Authors> "Publisher": "Springer"
<Date>2007</Date> }
<Publisher>Springer</Publisher> }
</Book>
| 11 |

Creating lists in XML and JSON

{
<Book> "Book":
<Title>Parsing Techniques</Title> {
<Authors> "Title": "Parsing Techniques",
<Author>Kathy Sierra</Author> "Authors": [ "Kathy Sierra", "Ceriel J.H. Jacobs" ],
<Author>Ceriel J.H. Jacobs</Author> "Date": "2007",
</Authors> "Publisher": "Springer"
<Date>2007</Date> }
<Publisher>Springer</Publisher> }
</Book>
| 14 |

An XML document is a tree

Book

Title Authors Date Publisher

Parsing
Author Author 2007 Springer
Techniques

Kathy Sierra Ceriel J.H.


Jacobs
| 15 |

A JSON Object is a tree

Book

Title Authors Date Publisher

[Kathy Sierra,
Parsing Ceriel J.H. 2007 Springer
Techniques Jacobs]
| 16 |

Trees are well-studied

The tree data structure has been well-studied by computer


scientists and mathematicians.
There are many well-known algorithms for processing and
traversing trees.
Both XML and JSON are able to leverage this.
| 17 |

XML Schema for Book


<xs:element name="Book">
<xs:complexType>
<xs:sequence>
<xs:element name="Title" type="xs:string" />
<xs:element name="Authors">
<xs:complexType>
<xs:sequence>
<xs:element name="Author" type="xs:string" maxOccurs="5"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Date" type="xs:gYear" />
<xs:element name="Publisher" minOccurs="0">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="Springer" />
<xs:enumeration value="MIT Press" />
<xs:enumeration value="Harvard Press" />
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
| 18 |

Equivalent JSON Schema


{
"$schema": http://json-schema.org/draft-04/schema",
"type": "object",
"properties": {
"Book": {
"type": "object",
"properties": {
"Title": {"type": "string"},
"Authors": {"type": "array", "minItems": 1, "maxItems": 5, "items": { "type": "string" }},
"Date": {"type": "string", "pattern": "^[0-9]{4}$"},
"Publisher": {"type": "string", "enum": ["Springer", "MIT Press", "Harvard Press"]}
},
"required": ["Title", "Authors", "Date"],
"additionalProperties": false
}
},
"required": ["Book"],
"additionalProperties": false
}
| 19 |

Title with string type


{
"$schema": "http://json-schema.org/draft-04/schema",
"type": "object",
"properties": { <xs:element name="Title" type="xs:string" />
"Book": {
"type": "object",
"properties": {
"Title": {"type": "string"},
"Authors": {"type": "array", "minItems": 1, "maxItems": 5, "items": { "type": "string" }},
"Date": {"type": "string", "pattern": "^[0-9]{4}$"},
"Publisher": {"type": "string", "enum": ["Springer", "MIT Press", "Harvard Press"]}
},
"required": ["Title", "Authors", "Date"],
"additionalProperties": false
}
},
"required": ["Book"],
"additionalProperties": false
}
| 20 |

Authors list
{
"$schema": "http://json-schema.org/draft-04/schema",
"type": "object", <xs:element name="Authors">
"properties": { <xs:complexType>
<xs:sequence>
"Book": {
<xs:element name="Author" type="xs:string" maxOccurs="5"/>
"type": "object", </xs:sequence>
"properties": { </xs:complexType>
"Title": {"type": "string"}, </xs:element>
"Authors": {"type": "array", "minItems": 1, "maxItems": 5, "items": { "type": "string" }},
"Date": {"type": "string", "pattern": "^[0-9]{4}$"},
"Publisher": {"type": "string", "enum": ["Springer", "MIT Press", "Harvard Press"]}
},
"required": ["Title", "Authors", "Date"],
"additionalProperties": false
}
},
"required": ["Book"],
"additionalProperties": false
}
| 21 |

Date with year type


{
"$schema": "http://json-schema.org/draft-04/schema",
"type": "object",
"properties": { <xs:element name="Date" type="xs:gYear" />
"Book": {
"type": "object",
"properties": {
"Title": {"type": "string"},
"Authors": {"type": "array", "minItems": 1, "maxItems": 5, "items": { "type": "string" }},
"Date": {"type": "string", "pattern": "^[0-9]{4}$"},
"Publisher": {"type": "string", "enum": ["Springer", "MIT Press", "Harvard Press"]}
},
"required": ["Title", "Authors", "Date"],
"additionalProperties": false
}
},
"required": ["Book"],
"additionalProperties": false
}
| 22 |

Publisher with enumeration


{
"$schema": "http://json-schema.org/draft-04/schema",
"type": "object",
"properties": {
"Book": {
"type": "object",
"properties": {
"Title": {"type": "string"},
"Authors": {"type": "array", "minItems": 1, "maxItems": 5, "items": { "type": "string" }},
"Date": {"type": "string", "pattern": "^[0-9]{4}$"},
"Publisher": {"type": "string", "enum": ["Springer", "MIT Press", "Harvard Press"]}
},
"required": ["Title", "Authors", "Date"],
"additionalProperties": false
} <xs:element name="Publisher" minOccurs="0">
<xs:simpleType>
}, <xs:restriction base="xs:string">
"required": ["Book"], <xs:enumeration value="Springer" />
"additionalProperties": false <xs:enumeration value="MIT Press" />
} <xs:enumeration value="Harvard Press" />
</xs:restriction>
</xs:simpleType>
</xs:element>
| 23 |

Bootstrap the schema language

An XML Schema is written in XML.


A JSON Schema is written in JSON.
| 24 |

Validate XML docs against XML Schema

XML
Schema XML
Schema XML instance is valid/invalid
Validator
XML
(instance)
| 25 |

Validate JSON docs against JSON Schema

JSON
Schema JSON
Schema JSON instance is valid/invalid
Validator
JSON
(instance)
| 26 |

JSON Schema validators

http://json-schema.org/implementations.html
| 27 |

Online JSON Schema validator


http://json-schema-validator.herokuapp.com/index.jsp

Paste your JSON Schema in here 1 Results of validation is


shown here 4

Paste your JSON in here 2

Click on the validate button 3


| 28 |

Command-line JSON Schema validator

Want to validate a JSON instance against a JSON Schema from


the command line? Download:
json-schema-validator-2.2.5-lib.jar
from:
https://bintray.com/fge/maven/json-schema-validator/view
and then, from a command line, type:
java -jar json-schema-validator-2.2.5-lib.jar schema.json instance.json
| 29 |

Use a JSON Schema validator from a Java


program
Want to validate a JSON instance against a JSON Schema from a
Java program? Download:
json-schema-validator-2.2.5-lib.jar
from:
https://bintray.com/fge/maven/json-schema-validator/view
and then read the documentation on the Java API.
| 30 |

Schema-for-Schemas

At the following URL is a JSON Schema. It is a schema for


validating JSON Schemas it is a schema for schemas!
http://json-schema.org/draft-04/schema#

JSON
Schema for
Schemas JSON
Schema JSON Schema is valid/invalid
JSON Validator
Schema
| 31 |

Status of JSON Schema specifications

JSON Schema is being developed under the auspicies of the


IETF standards organization.
The JSON Schema specification is currently at draft #4.
Work is proceeding on draft #5.
Draft #5 will contain a few new things, but will be mostly the
same as draft #4 (draft #5 is backward compatible with draft #4).
Here are the draft #5 changes:
https://groups.google.com/forum/#!topic/json-
schema/FSJmct8crXk
| 32 |

JSON Schema mailing list

The Google Group where JSON Schema is discussed:


https://groups.google.com/forum/#!forum/json-schema
| 33 |

JSON used to store location of tweets


(Eric Fisher)

https://www.mapbox.com/blog/twitter-map-every-tweet/
| 34 |

JSON used to store location of tweets (cont.)

Tracking geotagged tweets from Twitters public API for the last
three and a half years.
There are about 10 million public geotagged tweets every day,
which is about 120 per second.
The accumulated history adds up to nearly three terabytes of
compressed JSON and is growing by four gigabytes a day.
The map on the previous slide shows what 6,341,973,478 tweets
look like on a map.
Using this program to parse the JSON and pull out just each
tweets username, date, time, location, client, and text:

https://github.com/ericfischer/twitter-json
| 35 |

OASIS using JSON

The OASIS Open Data Protocol (Odata) Technical Committee


(https://www.oasis-open.org/committees/odata) is currently
defining a JSON format for OData service metadata, and plan to
use JSON Schema as the basis for this format.
| 36 |

Processing JSON and JSON Schema with


Java
Gson is a Java library that can be used to convert Java Objects into
their JSON representation. It can also be used to convert a JSON
string to an equivalent Java object.
https://code.google.com/p/google-gson/
http://www.studytrails.com/java/json/java-google-json-
introduction.jsp

Class JsonReader: Reads a JSON encoded value as a stream of


tokens. This stream includes both literal values (strings, numbers,
booleans, and nulls) as well as the begin and end delimiters of
objects and arrays. The tokens are traversed in depth-first order,
the same order that they appear in the JSON document. Within
JSON objects, name/value pairs are represented by a single token.
http://google-
gson.googlecode.com/svn/trunk/gson/docs/javadocs/com/google/g
son/stream/JsonReader.html
| 37 |

JSON Design Philosophy

StackOverflow Question:
Is there an XSLT equivalent for JSON? Something to allow me to
do transformations on JSON like XSLT does to XML.
Response: Not too sure there is need for this, and to me lack of
tools suggests lack of need. JSON is best processed as objects
(the way it's done in JS anyway), and you typically use language
of the objects itself to do transformations (Java for Java objects
created from JSON, same for Perl, Python, Perl, c#, PHP and so
on). Just with normal assignments (or set, get), looping and so
on. I mean, XSLT is just another language, and one reason it is
needed is that XML is not an object notation and thus objects of
programming languages are not exact fits (impedance between
hierarchic xml model and objects/structs). [StaxMan]

http://stackoverflow.com/questions/1618038/xslt-equivalent-for-json
| 38 |

http://www.freeformatter.com/xml-to-json-converter.html
| 39 |

Example of XML-to-JSON

The online freeformatter.com tool converts this XML:


<Book id="MCD">
<Title>Modern Compiler Design</Title>
<Author>Dick Grune</Author>
<Publisher>Springer</Publisher>
</Book>

to this JSON:
{
"@id": "MCD",
"Title": "Modern Compiler Design",
"Author": "Dick Grune",
"Publisher": "Springer"
}

I like that it encodes XML attributes by prefixing the attribute name


with the @ symbol.
| 40 |

Auto-converting XML to JSON: a bad idea?


Should you devise a way to auto-convert XML to JSON? In the below message the person says:

Based on our real-world experience, it is best to create the JSON design from scratch.
Do not auto-generate it from XML.

Hi all,

To throw in a view from a long-time XML user:


IPTC - www.iptc.org - builds XML-based news exchange formats for 17 years
now and was also challenged to do the same in JSON. After a long discussion
we refrained from automatically converting an existing XML data model to
JSON:
- currently no shared/common way to deal with namespaces in JSON
- designs like inline elements don't exist in JSON
- the element/attribute model has no corresponding design in JSON
- and a basic requirement of JSON users is: no complex data model, please!

Therefore we created
- a simplified data model for the news exchange in JSON - www.newsinjson.org
- compared to the richer but also more complex XML format www.newsml-g2.org
- a highly corresponding JSON model to an initial XML model for the rights
expression language ODRL as this is a set of data which cannot be
simplified: http://www.w3.org/community/odrl/work/json/ vs
http://www.w3.org/community/odrl/work/2-0-xml-encoding-constraint-draft-changes/

Both approaches were welcome and are used - and we learned: an XML-to-JSON
tool only is of limited help.
http://lists.xml.org/archives/xml-dev/201412/msg00022.html
| 41 |

The upcoming slides

The following slides are organized as follows:


Description of the JSON data format
How to create JSON Schemas
| 42 |

The JSON data format


| 43 |

JSON value

A JSON instance contains a single JSON value. A JSON value may


be either an object, array, number, string, true, false, or null:

Acknowledgement: This railroad graphic comes from the JSON specification, as do the railroad graphics on the next several slides.
| 44 |

JSON object

A JSON object is zero or more string-colon-value pairs, separated


by comma and wrapped within curly braces:

Example of a JSON object:


{ "name": "John Doe",
"age": 30,
"married": true }
| 45 |

Empty object

A JSON object may be empty.

This is a JSON object: { }


| 46 |

No duplicate keys

You should consider JSON objects as containing key/value


pairs.
Just as in a database the primary keys must be unique, so too in
a JSON object the keys must be unique.
This JSON object has duplicate keys:
{ "Title": "A story by Mark Twain",
"Title": "The Adventures of Huckleberry Finn" }
| 47 |

JSON parsers have unpredictable behavior


on JSON objects with duplicate keys

A JSON object whose names are all unique is


interoperable in the sense that all software
implementations receiving that object will
agree on the name-value mappings. When the
names within an object are not unique, the
behavior of software that receives such an
object is unpredictable. Many implementations
report the last name/value pair only. Other
implementations report an error or fail to parse
the object and some implementations report all
of the name/value pairs, including duplicates.

RFC 7159

This web site


(https://web.archive.org/web/20151004063037/http://fadefade.com/json-
comments.html) describes a JSON parser that ignores the first key/value
pair and as a result a form of covert channel is generated.
| 48 |

JSON array

A JSON array is used to express a list of values. A JSON array


contains zero or more values, separated by comma and wrapped
within square brackets:

Example of a JSON array


{ "name": "John Doe",
"age": 30,
"married": true,
"siblings": ["John", "Mary", "Pat"] }
| 49 |

Empty array vs. array with a null value

[] [ null ]
Array with one item in it.
Array with no items in it.
| 50 |

Array of objects

Each item in an array may be any of the seven JSON values.

{ The array contains 3 items.


"name": "John Doe", The first item is an object,
"age": 30, the second item is a boolean,
"married": true, and the third item is a string.
"siblings": [
{"name": "John", "age": 25},
true,
"Hello World"
Do Lab1
]
}
| 51 |

JSON number

A number is an integer or a decimal and it may have an exponent:

{
"name": "John Doe",
"age": 30, Example of a JSON number
"married": true,
"siblings": ["John", "Mary", "Pat"]
}
| 52 |

JSON string

A string is a sequence of Unicode characters wrapped within


quotes (").

{
"name": "John Doe", Example of a JSON string
"age": 30,
"married": true,
"siblings": ["John", "Mary", "Pat"]
}
| 53 |

JSON chars are a superset of XML chars

JSON

XML

Lesson Learned: be careful converting JSON to XML


as the result may be a non-well-formed XML document.
| 54 |

These characters must be escaped

If any of the following characters occur within a string, they must


be escaped by preceding them with a backslash (\):
quotation mark ("),
backslash (\),
the control characters U+0000 to U+001F
| 55 |

Each character corresponds to a number

ASCII table
| 56 |

Expressing characters in hex format

A character may be represented as a hexadecimal number using


this notation: \uXXXX
For example, instead of using 'j' in your JSON document, you can
use either \u006A or \u006a.
| 57 |

Unicode details

Use the \uXXXX notation for Unicode code points in the Basic
Multilingual Plane.
Use a 12-character sequence \uYYYY\uZZZZ to encode code
points above the Basic Multilingual Plane.
(0xD800 YYYY < 0xDC00 and 0xDC00 ZZZZ 0xDFFF)
| 58 |

No multiline strings

JSON does not allow multiline strings.

Legal:

{
"comment": "This is a very, very long comment"
}
Not legal:

{
"comment": "This is a very,
very long comment"
}
| 59 |

No multiline strings (cont.)

Legal:

{
"comment": "This is a very, \n very long comment"
}

Just 2 symbols
| 60 |

JSON parser converts \n to the newline


character
newline control character (invisible)

{
"comment": "This is a very, very long comment"
}

JSON parser

{
"comment": "This is a very, \n very long comment"
}
| 61 |

Achieving interoperability in a world where


different OS's represent newline differently

Each operating system has its own convention for signifying the end of a line of text:

Unix: the newline is a character, with the value hex A (LF).

MS Windows: the newline is a combination of two characters, with the values hex D (CR) and hex A
(LF), in that order.

Mac OS: the newline is a character, with the value hex D (CR).

This operating-system-dependency of newlines can cause interoperability problems, e.g., the


newlines in a string created on a Unix box will not be understood by applications running on a
Windows box.

Here is how the newline problem is resolved in XML and in JSON:

XML: all newlines are normalized by an XML parser to hex A (LF). So it doesn't matter whether you
create your XML document on a Unix box, a Windows box, or a Macintosh box, all newlines will be
represented as hex A (LF).

JSON: multi-line strings are not permitted! So the newline problem is avoided completely. You can,
however, embed within your JSON strings the \n (LF) or \r\n (CRLF) symbols, to instruct processing
applications: "Hey, I would like a newline here."

That is quite an interesting difference in approach between XML and JSON for dealing with the
newline problem!
| 62 |

JSON strings

http://json.org/

http://stackoverflow.com/questions/2392766/multiline-strings-in-json
| 63 |

Other JSON values

The values true, false, and null are literal values; they are not
wrapped in quotes.

{
"name": "John Doe",
"age": 30,
"married": true, Example of a JSON boolean
"siblings": ["John", "Mary", "Pat"]
}
| 64 |

Good use-case for null

Some people do not have a middle name, we can use null to


indicate no value:

{
first-name": "John",
middle-name": null,
last-name": Doe"
}
| 65 |

This is a legal JSON instance

42
| 66 |

so is this

"Hello World"
| 67 |

and so is this

true
| 68 |

and this

[ true, null, 12, "ABC" ]


| 69 |

Whitespace is irrelevant

{
"name": "John Doe",
"age": 30,
"married": true
}

equivalent

{"name":"John Doe","age":30,"married":true}
| 70 |

String delimiters: JSON vs. XML

JSON strings are always delimited by double quotes.


XML strings (such as attribute values) may be delimited by
either double quotes or single quotes.
| 71 |

JSON is recursively defined

{
"foo": json-value
}

The above JSON instance is an object. The


object has a single property, "foo". Its value is
any JSON value recursive definition!
| 72 |

Using JSON you can define arbitrarily


complex structures
{
"Book":
{
"Title": "Parsing Techniques",
"Authors": [ "Dick Grune", "Ceriel J.H. Jacobs" ]
}
}

{
"Book":
{
"Title": "Parsing Techniques",
"Authors": [
{"name":"Dick Grune", "university": "Vrije Universiteit"},
{"name":"Ceriel J.H. Jacobs", "university": "Vrije Universiteit"}
]
}
}
| 73 |

Extend, ad infinitum

{
"Book":
{
"Title": "Parsing Techniques",
"Authors": [
{"name": {"first":"Dick", "last":"Grune"},
"university": "Vrije Universiteit"},
{"name": {"first":"Ceriel", "last":"Jacobs"},
"university": "Vrije Universiteit"}
]
}
}
| 74 |

7 simple JSON components, assemble to


generate unlimited complexity

false

null

true

object array string number


| 75 |

JSON provides the structures and assembly


points, you customize them for your needs

object array
{ [ json-value, json-value, json-value, ]
"___": json-value,
"___": json-value, string
"___": json-value, "___"

}
| 76 |

JSON provides the structures and assembly


points, you customize them for your needs

structures

object array
{ [ json-value, json-value, json-value, ]
"___": json-value,
"___": json-value, string
"___": json-value, "___"

}
| 77 |

JSON provides the structures and assembly


points, you customize them for your needs

assembly points

object array
{ [ json-value, json-value, json-value, ]
"___": json-value,
"___": json-value, string
"___": json-value, "___"

}
| 78 |

JSON provides the structures and assembly


points, you customize them for your needs

object array
{ [ json-value, json-value, json-value, ]
"___": json-value,
"___": json-value, string
"___": json-value, "___"

}

customize
| 79 |

Comments not allowed!

You cannot comment a JSON instance document.


There is no syntax for commenting JSON instances.
Bummer.
| 80 |

oXygen XML supports JSON

You can create and edit JSON instances and JSON Schemas
using oXygen XML and it will check that the document is correctly
formatted. Nice!

Do Lab2
| 81 |

FAQ

Q: I don't see any JSON constructs that are equivalent to XML


attributes.
A: Correct, there are no attributes in JSON.
Q: Is there a namespace construct in JSON? If not, why was it
deemed unnecessary?
A: No namespaces in JSON. JSON is a reaction against the
complexity of XML and namespaces is one of the biggest
culprits, in terms of complexity.

Continued
| 82 |

FAQ

Q: Does JSON have tools to extract nodes from one or more


instances and recombine them into a new instance?
A: There is no equivalent of XSLT in JSON. The JSON people
believe that text documents should be processed by a general
purpose language such as Java or JavaScript, not a domain-
specific language such as XSLT.
Q: It appears to be trivially easy to convert from JSON to XML. Is
that correct or are there complicating factors?
A: JSON to XML should be straightforward, except when JSON
contains control characters (which are not allowed in XML).
Going the other way is challenging, due to XML attributes and
namespaces.

Acknowledgement: Thanks to Rob Simmons for the excellent questions.


| 83 |

Generating railroad diagrams

I love the railroad diagrams which are used to represent the


JSON language. What tool is used to generate these diagrams?
There is an Online Railroad Diagram Generator. It creates SVG
syntax diagrams, also known as railroad diagrams, from
context-free grammars specified in EBNF. You can copy the SVG
code or take screen shots.
You have to type in the grammar and it'll make the diagram.
| 84 |

Generating railroad diagrams

For example, to create the railroad diagram for a JSON object, you
would use the code:
object ::= '{' ((string ':' value ) ( ',' string ':' value )*)? '}'

http://stackoverflow.com/questions/796824/tool-for-generating-railroad-diagram-used-on-json-org
| 85 |

JSONx (JSON XML)

JSONx is an IBM standard format to represent JSON as XML

This document specifies a mapping


between JSON and XML, known as
JSONx. It is used by several IBM products.

http://tools.ietf.org/html/draft-rsalz-jsonx-00

"phoneNumbers": [ <json:array name="phoneNumbers">


"212 555-1111", <json:string>212 555-1111</json:string>
"212 555-2222" <json:string>212 555-2222</json:string>
] </json:array>
| 86 |

xml2json

XML Shell (xmlsh, http://www.xmlsh.org) provides a capability to


convert XML to JSON: http://www.xmlsh.org/CommandXml2json
| 87 |

Interoperable JSON (I-JSON)

Earlier we saw how duplicate keys in JSON objects cause


interoperability problems.
That is described in I-JSON, along with a lot of other things that
can impact interoperability in JSON data exchanges.
Here is the I-JSON specification:
http://www.rfc-editor.org/rfc/rfc7493.txt
| 88 |

How to create JSON Schemas


| 89 |

MS Visual Studio supports JSON Schema

Visual Studio provides a wonderful editor that helps you create


JSON Schemas.
The editor is available in the free Community version of Visual
Studio 2013.
Here's a YouTube presentation on how to create JSON Schemas
using Visual Studio:
https://www.youtube.com/watch?v=Jt5SCNC87d4
| 90 |

A contract for data exchanges

Both XML Schema and JSON Schema may be used as a contract


for data exchanges:

Contract
(schema)
conforms to

data
| 91 |

Fundamental difference between


XML Schema and JSON Schema
XML Schema: specifies closed content unless deliberate
measures are taken to make it open (e.g., sprinkle the <any>
element liberally throughout the schema).
JSON Schema: specifies open content unless deliberate
measures are taken to make it closed (e.g., sprinkle
"additionalProperties": false liberally throughout the schema).

Definition of Open Content: instance


documents can contain items above and
beyond those specified by the schema.

Definition of Closed Content: instance


documents can contain only those items
specified by the schema.
| 92 |

The power of JSON Schema comes from:

Regular expressions (regexes): JSON Schema uses regexes a lot.


The regular expression language is well-established and very
powerful. Most data constraints can be expressed using regexes.
Recursion: nearly all keywords in JSON Schema are recursively
defined, so you are limited only by your imagination in what you
create.
Simplicity: JSON Schema doesn't have a large set of data types or
other features. It employs simple components which may be
assembled to generate arbitrary complexity.
| 93 |

The power of JSON Schema comes from:

Regular expressions (regexes): JSON Schema uses regexes a lot.


The regular expression language is well-established and very
powerful. Most data constraints can be expressed using regexes.
Recursion: nearly all keywords in JSON Schema are recursively
defined, so you are limited only by your imagination in what you
create.
Simplicity: JSON Schema doesn't have a large set of data types or
other features. It employs simple components which may be
assembled to generate arbitrary complexity.

Awesome
| 94 |

A JSON Schema is a JSON object

{
"keyword": value,
"keyword": value,
"keyword": value,

}
| 95 |

A JSON Schema is a JSON object

{
"keyword": value,
"keyword": value,
"keyword": value,

}

The JSON Schema specification defines


the set of keywords and values that can
be used to construct a schema. In this
tutorial we will learn the keywords that
may be used in a JSON Schema.
| 96 |

Empty schema

{}

This is a valid JSON Schema!


It places no restrictions on
JSON instances.
| 97 |

Every JSON instance is valid!

{}
JSON
Schema Valid!
Validator
JSON
instance

Do Lab3
| 98 |

"$schema" keyword

{
"$schema": "http://json-schema.org/draft-04/schema#",

}
| 99 |

"$schema" keyword

{
"$schema": "http://json-schema.org/draft-04/schema#",

}

The $schema keyword says: This object is a


JSON schema, conforming to the schema
at http://json-schema.org/draft-04/schema#.
| 100 |

"type" keyword

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "type",

}
| 101 |

"type" keyword

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "type",

}

The type keyword says: The JSON instance


must be of this type. There are seven types:
- "boolean"
- "number"
- "integer"
- "string"
- "array"
- "object"
- "null"
| 102 |

Boolean type

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "boolean"
}

This schema constrains instances


to contain only a boolean value
(true or false).
| 103 |

Only boolean instances are valid

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "boolean" true
}

JSON
Schema
Validator

Valid!
| 104 |

String instances are not valid

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "boolean" "Hello World"
}

JSON
Schema
Validator

Not valid!
| 105 |

Alert! "true" is not a Boolean value

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "boolean" "true"
}

JSON
Schema
Validator

Not valid!
| 106 |

Run example01

Open the example01 folder (in the examples folder).


In that folder are two files:
schema.json
instance.json
Open the files in oXygen XML.
Copy to the clipboard the content of schema.json and then
paste it into the Schema section of this online validator:
http://json-schema-validator.herokuapp.com/index.jsp
Copy to the clipboard the content of instance.json and then
paste it into the Data section.
Press the validate button.
| 107 |
http://json-schema-validator.herokuapp.com/index.jsp

Paste schema here 1 Instance is valid!


4

Paste instance here 2

Press the Validate button 3


| 108 |

Order of keywords is irrelevant

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "boolean"
}

equivalent!

{
"type": "boolean",
"$schema": "http://json-schema.org/draft-04/schema#"
}
| 109 |

Number type

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "number"
}

This schema constrains instances


to contain only a number (integer,
decimal, number with exponent).
| 110 |

Only numeric instances are valid

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "number" 12.3
}

JSON
Schema
Validator

Valid!
| 111 |

Examples of numeric values

12 99.1 12.123e3 12.123E3


12.123 x 103
| 112 |

"enum" keyword

The value of enum is an array.


The items in the array is a list of values that instances may have.
The following schema says that the only allowable values in
instances are the numbers: 2, 4, 6, 8, 10.

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "number",
"enum": [2, 4, 6, 8, 10]
}
See example02
| 113 |

"minimum" and "maximum" keywords

A range of values can be specified using the "minimum" and


"maximum" keywords.
The following schema constrains instances to numbers in the
range 0-100, inclusive.

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "number",
"minimum": 0,
"maximum": 100
}
| 114 |

"exclusiveMinimum", "exclusiveMaximum"

A range's endpoints can be made exclusive by using the


"exclusiveMinimum" and "exclusiveMaximum" keywords.
The following schema constrains instances to numbers in the
range 0-100, exclusive.

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "number",
"minimum": 0,
"maximum": 100,
"exclusiveMinimum": true,
"exclusiveMaximum": true
}
See example03
| 115 |

"multipleOf" keyword

Instances can be constrained to a number that is a multiple of a


number.
The following schema says that a JSON instance must be a
number 0-100, inclusive, and must be a multiple of 2.

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "number",
"minimum": 0,
"maximum": 100,
"multipleOf": 2
}

Here are four schema-valid values: 0 2 4 100

See example04
| 116 |

"multipleOf" value

The value of "multipleOf" must be greater than 0 (no negative


numbers).
| 117 |

Integer type

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "integer"
}

This schema constrains instances to contain only an


integer.

Here are two schema-valid values: -900 129


| 118 |

Number constraints also apply to integer

The constraints that apply to "number" also apply to "integer":


enum
minimum
maximum
exclusiveMinimum
exclusiveMaximum
multipleOf

Do Lab4
| 119 |

String type

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "string"
}
This schema constrains instances to contain only a
string.
| 120 |

"maxLength" keyword

The maximum length of a string is constrained using the


"maxLength" keyword.

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "string",
"maxLength": 20
}

Here is a schema-valid value: "Hello World"

See example05
| 121 |

Value of "maxLength"

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "string",
"maxLength": ___
}

Must be an integer, 0
| 122 |

"minLength" keyword

The "minLength" keyword is used to specify the shortest string


length allowed.
The value of "minLength" must be an integer, greater than or
equal to 0.
The default value is 0.
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "string",
"minLength": 5,
"maxLength": 20
}
| 123 |

"pattern" keyword

The set of characters that can be used in a string can be


constrained using the "pattern" keyword, whose value is a
regular expression [1].
The following schema constrains the set of characters to the
lower- and upper-case letters of the English alphabet, plus the
space character.
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "string",
"maxLength": 20,
"pattern": "^[a-zA-Z ]*$"
}
See example06

[1] What flavor of regexes does JSON Schema use? Answer: ECMA 262.
http://www.ecma-international.org/ecma-262/5.1/#sec-15.10
| 124 |

"pattern" value is a regular expression

The value of "pattern" is a regular expression.


The regular expressions are not implicitly anchored (as they are
in XML Schema) so you must use the start and end anchors
(^$).

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "string",
"maxLength": 20,
"pattern": "^[a-zA-Z ]*$"
}

This symbol indicates that a string must end


with the letters of the alphabet plus space.

This symbol indicates that a string must start


with the letters of the alphabet plus space.
| 125 |

How to read this JSON Schema

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "string",
"maxLength": 20,
"pattern": "^[a-zA-Z ]*$"
}

The string in a JSON instance cannot have a length greater than


20 characters and the characters must be a-z, A-Z, or space.


| 126 |

Components of regular expressions


Basic pattern Matching string
x The character x

. Any character, except newline


Legend:
[xyz ...] Any of the characters, x, y, z, x, y, z, stand for any character
R, R1, R2, stand for any regular
expression
Repetition operators:

R? R is optional (zero or one Operator Precedence:


occurrence) Repetition operators have the
R* Zero or more occurrences of R highest precedence (bind most
tightly); next comes the
R+ One or more occurrences of R concatenation operator; and the
alternatives operator | has the
lowest precedence

Compositional operators:

R1R2 An R1 followed by an R2

R1|R2 Either an R1 or an R2
Acknowledgement: This table comes
from Modern Compiler Design by
Grune et al, p62.
Grouping:

(R) R itself
| 127 |

Illustrating the Risk of Unconstrained Strings

One way to reduce the likelihood that a string can contain unauthorized or malicious content is to constrain the
contents of the string. As the following figure shows, there are two dimensions to constraining strings: (1)
constrain the length of the string, and (2) constrain the size of the character set. In general, the risk that a
constrained string could contain unauthorized or malicious content increases as the permissible string length
increases and/or the size of the permissible character set increases.
| 128 |

Escaping the dot (.) and the dash (-)

In regular expressions the dot (.) symbol means any


character
Suppose that you want the dot (.) symbol, not any character.
How would you express that? You might think, do this: \.
However, thats not correct. You need to escape the backslash.
Heres how to do it: \\.
Similarly, in regular expressions the dash (-) symbol means
range-of-characters
Suppose that you want the dash (-) symbol, not range-of-
characters. Heres how to express it: \\-
| 129 |

Here's why you double-escape

JSON schema
validator

The validator gets the


"pattern" : "^... \. ...$" parsed schema and the
parser has converted \\
to \

JSON parser

"pattern" : "^... \\. ...$"


| 130 |

Enumerate the allowed string

The "enum" keyword can be used with the string type.


The following schema says that only three strings are allowed:
"red", "white", or "blue".

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "string",
"enum": ["red", "white", "blue"]
}

Heres a schema-valid instance: "red"

See example07
| 131 |

"enum" applies to all types

The "enum" keyword can be used with all seven types.


| 132 |

"enum" without "type"

When the "type" keyword" is specified, then all the values in the
"enum" array must conform to that type.
If the "type" keyword is omitted, then the values in the "enum"
array can be of any type.
The following schema says that a JSON instance can be the
string: "red", the number 12, or the boolean false.

{
"$schema": "http://json-schema.org/draft-04/schema#",
"enum": ["red", 12, false]
}
| 133 |

"format" keyword

The "format" keyword is used to specify the format of a string.


The following schema says that JSON instances must consist of
a string and the string must be formatted as an ISO 8601
date/time value.
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "string",
"format": "date-time"
}

Heres a schema-valid instance: "2014-06-20T12:50:00Z"

See example08
| 134 |

date-time format

A string with format date-time must conform to one of these two


forms:
yyyy-MM-dd'T'HH:mm:ssZ
yyyy-MM-dd'T'HH:mm:ss.SSSZ
| 135 |

"format" keyword values

Here are the valid values for the "format" keyword:


"date-time"
"email"
"hostname"
"ipv4"
"ipv6"
"uri"
Those values are called format attributes.
The "format" keyword may only be used with the string type.
| 136 |

Examples of "format" values

"date-time"
"2014-06-20T12:50:00Z"
"email"
"smith@example.org"
"hostname"
"www.google.com"
"ipv4"
"192.168.5.0"
"ipv6"
"2001:db8:a0b:12f0::1"
"uri"
"http://www.google.com" Do Lab5
| 137 |

Array
| 138 |

Array type

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "array"
}
| 139 |

Array type

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "array"
}

This schema constrains instances


to contain only an array.

Heres a schema-valid value:


["value1", "value2", 12, null]
| 140 |

Enumerate the allowed arrays

The "enum" keyword can be used with the array type.


The following schema says that instances may be either of these
arrays: ["A", "B"] or [1,2,3].

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "array",
"enum": [["A", "B"], [1,2,3]]
}

Here are the only two valid instance values: ["A", "B"] [1,2,3]

See example09
| 141 |

"maxItems" keyword

The maximum number of items in the array is specified using


the "maxItems" keyword.
The following schema says that JSON instances must consist of
an array and the instance cannot contain more than three items.
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "array",
"maxItems": 3
}

Heres a schema-valid instance: [ 1, true, ["A", "B"] ]

See example10
| 142 |

The items can be anything

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "array",
"maxItems": 3
}
This constrains the number of array items to three.
But each of those items can be anything.
| 143 |

"items" keyword

The items in an array can be constrained using the "items"


keyword.
The value of "items" is a JSON Schema.

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "array",
"maxItems": 3,
"items": JSON Schema
}
| 144 |

Recursive definition!

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "array",
This JSON
Schema "maxItems": 3,
contains this "items": JSON Schema
JSON }
Schema
| 145 |

All that weve learned applies to the


subschema

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "array",
"maxItems": 3,
"items": {
"keyword": value,
"keyword": value,
"keyword": value,

}
}
The value of "items" can be all the things
that we've seen: the "type" keyword, the
"maxLength" keyword for strings, the
"minimum" and "maximum" keywords for
numbers, and so forth.
| 146 |

Terminology: root schema, subschema

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "array",
"maxItems": 3,
"items": {{
"keyword":
"keyword":value,
value,
"keyword":
"keyword":value,
value,
"keyword":
"keyword":value,
value,

}}
}

subschema
root schema
| 147 |

Dont use $schema in subschemas

{
Yes "$schema": "http://json-schema.org/draft-04/schema#",
"type": "array",
"maxItems": 3, No
"items": {
$schema": "http://json-schema.org/draft-04/schema#",
"keyword": value,
"keyword": value,

}
}

"$schema" should only be used in the root schema, not in


subschemas. Its not illegal in draft #4, but in draft v5,
$schema appearing in a subschema will be an error.
Do Lab6
| 148 |

Default value for "items"

If "items" is not present, it has a default value of { }, which means


that each item in the array may be any value.
| 149 |

Max of 3 items, each an integer

The following schema says that instances must not contain more
than 3 items and each item must be an integer.

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "array",
"maxItems": 3,
"items": {
"type": "integer"
}
}

Here are four schema-valid instances: [1,2,3] [1,2] [1] []

See example11
| 150 |

Max of 3 items, each an integer 0 100

The following schema says that instances must not contain more
than 3 items and each item must be an integer and the integer
must be between 0 and 100.

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "array",
"maxItems": 3,
"items": {
"type": "integer",
"minimum": 0,
"maximum": 100
}
}

See example12
| 151 |

"uniqueItems" keyword

We can require each item in the array be unique. This is


accomplished using the "uniqueItems" keyword.

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "array",
"maxItems": 3,
"items": {
"type": "integer",
"minimum": 0,
"maximum": 100
},
"uniqueItems": true
}

The default value of "uniqueItems" is false.


See example13
| 152 |

Max of 3 items, each a string with max length


20, consisting of letters and spaces only

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "array",
"maxItems": 3,
"items": {
"type": "string",
"maxLength": 20,
"pattern": "^[a-zA-Z ]*$"
}
}

See example14
| 153 |

"items" with empty schema value

If the value of "items" is { } then each item may have any value.
The following schema permits each array item to be anything.

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "array",
"maxItems": 3,
"items": {}
}

Here is a schema-valid instance: [ "hello world", 12, {"age":30} ]

See example15
| 154 |

Equivalent!

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "array",
"maxItems": 3,
"items": {}
}

Equivalent since the default


value of "items" is { }

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "array",
"maxItems": 3
}
| 155 |

Two legal values for "items"

As weve seen, the value of "items" is an object (a JSON


Schema).
Alternatively, the value of "items" may be an array. Each item in
the array must be an object (a JSON Schema).
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "array",
"items": JSON Schema
}

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "array",
"items": [ This kind of array is
JSON Schema, called an array tuple
JSON Schema,

]
}
| 156 |

Array may contain two strings

The below schema specifies an array consisting of up to two


items.
If the first item is present, it must be either the string "white" or
"black".
If the second item is present, it must be either the string "cup"
or "plate".
The array may be empty.
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "array",
"items": [
{ "type": "string", "enum": ["white", "black"] },
{ "type": "string", "enum": ["cup", "plate"] }
]
}
| 157 |

Some legal instances

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "array",
"items": [
{ "type": "string", "enum": ["white", "black"] },
{ "type": "string", "enum": ["cup", "plate"] }
]
}

Here are three valid values: [ "white", "cup"] [ "white"] []

See example16

Do Lab7
| 158 |

Array has open content!

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "array",
"items": [
{ "type": "string", "enum": ["white", "black"] },
{ "type": "string", "enum": ["cup", "plate"] }
]
}

This schema specifies what the first two items in the array must
be, but it doesnt say there cant be additional items.

This is a valid value: [ "white", "cup", true, null, {"foo": 12} ]

additional items
| 159 |

"additionalItems" keyword

"additionalItems": false is used to specify that an array cannot


contain additional items.
Now the array is constrained to no more than two items:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "array",
"items": [
{ "type": "string", "enum": ["white", "black"] },
{ "type": "string", "enum": ["cup", "plate"] }
],
"additionalItems": false
}
See example17

Do Lab8
| 160 |

Array tuple with { } for each value

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "array",
"items": [
{},
{}
],
"additionalItems": false
}

The array must contain up to two items. Each item can be


anything: a number, integer, boolean, array, object, or null.

Here are 4 valid instances: [ ] [1] [1, true] [1, [1, true]]

See example18
| 161 |

Desire: any number of additional items, but of


a certain type
If we dont specify "additionalItems": false, then instances can
have any number of additional items and each item can be
anything (of any type).
We may want to allow any number of additional items, but they
are to be constrained to be of a certain type (e.g., constrain them
to be booleans)
| 162 |

additionalItems with a JSON Schema value

We have seen that additionalItems can have a boolean value.


Alternatively, it can have a value that is an object (JSON
Schema)

"additionalItems": JSON Schema


| 163 |

Any number of boolean values

The below schema specifies that an instance must be an array


that contains one item: a string of max length 20, consisting of
the lower and uppercase letters plus space.
Further, any number of boolean values may follow the string.
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "array",
"items": [
{ "type": "string", "maxLength": 20, "pattern": "^[a-zA-Z ]*$" }
],
"additionalItems": {"type": "boolean"}
}

Here are three valid instances: ["Hello World", true, false, false, true]
["Hello World"]
[]
See example19
| 164 |

Restrict the additional items

On the previous slide the schema allowed an unbounded


number of additional items (i.e., boolean values).
We can restrict the total number of items (thus restricting the
number of additional items) using the "maxItems" keyword:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "array",
"items": [
{ "type": "string", "maxLength": 20, "pattern": "^[a-zA-Z ]*$" }
],
"additionalItems": {"type": "boolean"},
"maxItems": 3
}

The total number of items that are allowed in the array is 3,


which means there can only be 2 boolean values.

See example19.1
| 165 |

Valid and invalid instances

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "array",
"items": [
{ "type": "string", "maxLength": 20, "pattern": "^[a-zA-Z ]*$" }
],
"additionalItems": {"type": "boolean"},
"maxItems": 3
}

These are schema-valid: These are not schema-valid:


[] [true]
["Hello World"] ["Hello World", true, false, false]
["Hello World", true]
["Hello World", true, false]
| 166 |

Restrictions on "additionalItems": { }

The value of "additionalItems" can be a schema only if "items" is


present and its value is an array tuple (an array of schemas).
| 167 |

Default value of "additionalItems" is { }

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "array",
"items": [
{ "type": "string", "enum": ["white", "black"] },
{ "type": "string", "enum": ["cup", "plate"] }
]
}
The array may contain zero
or more additional items
Equivalent and each additional item
may have any value.
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "array",
"items": [
{ "type": "string", "enum": ["white", "black"] },
{ "type": "string", "enum": ["cup", "plate"] }
],
"additionalItems": { }
}
| 168 |

Rules for constraining arrays

If the value of "items" is a JSON Schema, then we can constrain


the number of items using "maxItems"
If the value of "items" is an array, then we can prevent the
presence of additional items using "additionalItems": false
If the value of "items" is an array and the value of "additionalItems"
is a JSON Schema, then we can constrain the total number of
items using "maxItems"
| 169 |

How to constrain arrays:


If the schema has this: then the array can be
constrained using this
keyword:
"type": "array" "enum"
"type": "array", "maxItems"
"items": JSON Schema
"type": "array", "additionalItems": false
"items": [ JSON Schema,
JSON Schema,

]
"type": "array", "maxItems"
"items": [ JSON Schema,
JSON Schema,

],
"additionalItems": JSON Schema
| 170 |

The value of additionalItems is a choice

"type": "boolean"
"additionalItems":
JSON Schema
| 171 |

Here is how the schema-for-schema defines


"additionalItems"

"additionalItems": {
"anyOf": [
{ "type": "boolean" },
{ "$ref": "#" }
],
"default": {}
},
| 172 |

Object
| 173 |

Object type

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object"
}
| 174 |

Object type

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object"
}
The schema constrains instances to contain only an object.

Heres a schema-valid value:


{
"X": 12,
"Y": "hello"
}

See example20
| 175 |

Unconstrained object

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object"
}

The schema specifies no constraints on the object's properties


(i.e., name/value pairs).
| 176 |

Enumerate the allowed objects

The "enum" keyword can be used to enumerate the objects that


are allowed in instances.
The following schema enumerates two objects:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"enum": [
{"name": "John Doe", "age": 30},
{"company": "Google", "product": "searching"}
]
}

Here are the only valid instances:

{"name": "John Doe", "age": 30} {"company": "Google", "product": "searching"}

See example21
| 177 |

Desire: specify the property names, but


values are variable
The "enum" keyword specifies both property name and property
value.
For more flexibility we would like to enumerate the property
names but leave the property values variable.
| 178 |

"properties" keyword

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"}
}
}

Instances must be an object. The object may contain a property


"name" and a property "age". The value of "name" must be a string.
The value of "age" must be an integer.

See example22

Do Lab9
| 179 |

Sample instance

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"}
}
}

Here is a valid instance:


{
"name": "John Doe",
"age": 30
}
| 180 |

Order of properties is irrelevant

{
"name": "John Doe",
"age": 30
}

equivalent instances

{
"age": 30,
"name": "John Doe"
}
| 181 |

Property name can be an empty string

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
"": {"type": "boolean"}
}
}

Here is a valid instance:


{
"name": "John Doe",
"age": 30,
"": true
}

See example23
| 182 |

Properties are optional

By default, the properties listed in "properties" are optional.


These are all valid instances:

{ { {}
"name": "John Doe", "name": "John Doe",
"age": 30, "age": 30
"": true }
}

{ {
"name": "John Doe", "name": "John Doe"
"": true }
}
| 183 |

Mandate properties using "required"

The "required" keyword is used to mandate properties.


The value of "required" is an array of strings, each string
corresponding to the name of a property.

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"name": { "type": "string" },
"age": { "type": "number" }
},
"required": ["name", "age"]
}

Instances must have "name" and "age" properties.

See example24
| 184 |

Object has open content by default


{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"name": { "type": "string" },
"age": { "type": "number" }
},
"required": ["name", "age"]
}

The schema specifies two properties that must be in


instances. But it doesnt say there cant be additional
properties. This is a valid value:
{
"name": "John Doe",
"age": 30,
"height": 68,
additional properties
"married": true
}
| 185 |

Sprinkle additional properties anywhere

In instances "name" and "age" dont have to come first.


Additional properties can come before and after them.
This is a valid instance:
{
"married": true,
"name": "John Doe",
"height": 68,
"age": 30
}
| 186 |

"additionalProperties" keyword

Instances can be constrained to contain only those listed in the


schema by using "additionalProperties": false
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"name": { "type": "string" },
"age": { "type": "number" }
},
"required": ["name", "age"],
"additionalProperties": false
}

Now instances must contain "name" and


"age" and nothing else.

See example25 Do Lab10


| 187 |

Well-constrained object

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"name": { "type": "string", "maxLength": 20, "pattern": "^[a-zA-Z ]*$" },
"age": { "type": "number", "minimum": 0, "maximum": 120 }
},
"required": ["name", "age"],
"additionalProperties": false
}

Instances must contain an object. The object must have exactly


two properties: one named "name" and the other named "age".
The value of "name" must be a string, not longer than 20
characters, consisting of the symbols a-z, A-Z, and space. The
value of "age" must be a number in the range 0 120, inclusive.
Nice!

See example26
| 188 |

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"name": { "type": "string", "maxLength": 20, "pattern": "^[a-zA-Z ]*$" },
name and "age": { "type": "number", "minimum": 0, "maximum": 120 }
age are },
required "required": ["name", "age"],
"additionalProperties": false
}
| 189 |

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"name": { "type": "string", "maxLength": 20, "pattern": "^[a-zA-Z ]*$" },
"age": { "type": "number", "minimum": 0, "maximum": 120 }
No other
},
properties are "required": ["name", "age"],
allowed "additionalProperties": false
}
| 190 |

The value of name must be a string, with max


length of 20, consisting of a-z, A-Z, and
space.

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"name": { "type": "string", "maxLength": 20, "pattern": "^[a-zA-Z ]*$" },
"age": { "type": "number", "minimum": 0, "maximum": 120 }
},
"required": ["name", "age"],
"additionalProperties": false
}
| 191 |

The value of age must be a number in the


range 0 120, inclusive.

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"name": { "type": "string", "maxLength": 20, "pattern": "^[a-zA-Z ]*$" },
"age": { "type": "number", "minimum": 0, "maximum": 120 }
},
"required": ["name", "age"],
"additionalProperties": false
}
| 192 |

The instance must be an object.

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"name": { "type": "string", "maxLength": 20, "pattern": "^[a-zA-Z ]*$" },
"age": { "type": "number", "minimum": 0, "maximum": 120 }
},
"required": ["name", "age"],
"additionalProperties": false
}
| 193 |

Must specify "type": "object" with "properties"

When "properties" is used, then "type": "object" is required to be


present. If it is not present:

{
"$schema": "http://json-schema.org/draft-04/schema#",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"}
}
}

then any value in the instance is valid. For example the following
instance validates against the schema:

true

See example27
| 194 |

Here's why

{
"$schema": "http://json-schema.org/draft-04/schema#",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"}
}
}

This schema does not specify the type of value


that an instance must have, so instances can have
any type!
| 195 |

Summary of "properties"

The value of "properties" is an object. The object contains


members. Each member has a name and its value is a schema:

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"property-name": JSON Schema,
"property-name": JSON Schema,
...
}
}
| 196 |

Recursive definition!

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
This JSON
Schema "properties": {
contains "property-name": JSON Schema,
these JSON "property-name": JSON Schema,
Schemas ...
}
}
| 197 |

additionalItems versus additionalProperties

Careful! Distinguish between "additionalItems": false and


"additionalProperties": false:
The former is used to disallow additional items in an array, the
latter is used to disallow additional properties in an object
| 198 |

Constraining the number of properties

The minimum and maximum number of properties can be


constrained using the "minProperties" and "maxProperties"
keywords.
The following schema specifies a list of activities: bowling,
golfing, and kayaking. At least one activity but not more than
two must be selected for inclusion in a JSON instance:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"bowling": { "type": "string", "maxLength": 20 },
"golfing": { "type": "string", "maxLength": 20 },
"kayaking": { "type": "string", "maxLength": 20 }
},
"minProperties": 1,
"maxProperties": 2,
"additionalProperties": false
}

See example28
| 199 |

Sample instance

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"bowling": { "type": "string", "maxLength": 20 },
"golfing": { "type": "string", "maxLength": 20 },
"kayaking": { "type": "string", "maxLength": 20 }
},
"minProperties": 1,
"maxProperties": 2,
"additionalProperties": false
}
This JSON instance is valid since it has two of the activities:

{
"bowling": "50 Lanes",
"kayaking": "Red Rock River"
}
| 200 |

Property with { } as its value

If the value of a property is { } then that property may have any


value.
The following schema says the value of "name" and "age" can
be anything.
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"name": { },
"age": { }
},
"required": ["name", "age"],
"additionalProperties": false
}
Here is a valid value: {
"name": 12,
"age": false
}
See example29
| 201 |

"additionalProperties" (revisited)

The value of "additionalProperties" may be a boolean, as shown


on the previous slides.
Or, it may be a JSON Schema:
"additionalProperties": JSON Schema
Here is an example:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"}
},
"required": ["name", "age"],
"additionalProperties": {
"type": "boolean"
}
}
See example30
| 202 |

Additional properties must be booleans

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"}
},
"required": ["name", "age"],
"additionalProperties": {
"type": "boolean"
}
}

The "additionalProperties" specifies: "Instance may have properties


beyond just "name" and "age. But any additional properties must
have a boolean value."

So, "additionalProperties" (with an object value) is just for specifying


the type of additional properties.
| 203 |

Instance containing additional boolean


properties

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"}
},
"required": ["name", "age"],
"additionalProperties": {
"type": "boolean"
}
}

Here is a valid value: {


"name": "John Doe",
"age": 30,
"married": true, Notice that each additional property
"living": true has a boolean value.
}
| 204 |

Unlimited number of additional properties

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"}
},
"required": ["name", "age"],
"additionalProperties": {
"type": "boolean"
}
}

As weve seen, instances may contain additional boolean properties.


How many? Answer: An unlimited number.
| 205 |

Constrain the number of additional properties


using "maxProperties"

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"}
},
"required": ["name", "age"],
"additionalProperties": {
"type": "boolean"
},
"maxProperties": 4
}

Now instances can have, at most, 4 properties.

See example31
| 206 |

Valid instances

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"}
},
"required": ["name", "age"],
"additionalProperties": { {
"type": "boolean"
},
"name": "John Doe",
"maxProperties": 4 "age": 30
} } 3

Valid instances: { {
"name": "John Doe", "name": "John Doe",
"age": 30, "age": 30,
"married": true, "married": true,
"living": true } 2
} 1
| 207 |

But this is also a valid instance

{
"name": "John Doe",
"age": 30,
"put-secret-information-in-this-name-field": true,
}

The value of additional


properties are constrained
to boolean

No constraint on the property name!


| 208 |

Constrained additional properties

Use the patternProperties keyword:


{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"}
},
"required": ["name", "age"],
"patternProperties": {
"^(married|divorced|living)$": {"type": "boolean"}
},
"additionalProperties": false
}

See example31.1 Do Lab11


| 209 |

Regex for the property name

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"}
},
"required": ["name", "age"],
"patternProperties": {
"^(married|divorced|living)$": {"type": "boolean"}
},
"additionalProperties": false
}

Property names must conform to this regular expression (regex)


| 210 |

Valid, invalid instance

Valid instance:
{
"name": "John Doe",
"age": 30,
"married": true,
"living": true
}

Invalid instance:
{
"name": "John Doe",
"age": 30,
"put-secret-information-in-this-name-field": true,
}
| 211 |

Constraining objects:

If the schema has this: then the object can be


constrained using this
keyword:

"type": "object" "enum"

"type": "object", "additionalProperties": false


"properties": { }

"type": "object", "maxProperties"


"properties": { }
"additionalProperties": JSON Schema
| 212 |

Multiple Types
| 213 |

Instance may be a boolean or a number

The value of "type" can be an array of types.


This schema says that JSON instances must contain either a
boolean value or a number:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": [ "boolean", "number" ]
}

Here are two valid values: true 12

See example32

Do Lab12
| 214 |

Instance may be an array or an object

This says that JSON instances must be either an array or an


object:
"type": ["array", "object"]
The following schema constrains the array, if present, to 3
integers in the range 0-100, and constrains the object, if present,
to exactly two properties name and age and no others:

Next slide
| 215 |

Well-constrained array or object

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": ["array", "object"],
"maxItems": 3,
"items": {
"type": "integer",
"minimum": 0,
"maximum": 100
},
"properties": {
"name": { "type": "string", "maxLength": 20, "pattern": "[a-zA-Z ]+" },
"age": { "type": "integer", "minimum": 0, "maximum": 100 }
},
"required": ["name", "age"],
"additionalProperties": false
}
| 216 |

Well-constrained array or object

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": ["array", "object"],
"maxItems": 3,
"items": {
"type": "integer",
"minimum": 0,
"maximum": 100
},
"properties": {
"name": { "type": "string", "maxLength": 20, "pattern": "[a-zA-Z ]+" },
"age": { "type": "integer", "minimum": 0, "maximum": 100 }
},
"required": ["name", "age"],
"additionalProperties": false
}

Here are two valid values: [12, 3, 99] {


"name": "John Doe",
"age": 30
See example33 }
| 217 |

Annotating Schemas
| 218 |

Comments for humans

The "description" keyword is used to provide a human-readable


description. The value of "description" is any string. It is ignored in
validation. The "title" keyword is a short human-readable title.

{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Short title",
"description": "This description can be arbitrarily long "
}

These keywords may be used in the root schema and in


subschemas.
| 219 |

Nice example of using description and title

{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "map.feature.plot",
"description": "Plots feature data on the map.",
"type": "object",
"properties": {
"overlayId": {
"description": "The ID of the overlay this feature should be loaded into. If an overlay with this ID already exists, the new
feature is merged into existing overlay; otherwise, a new overlay is created. If no overlayId is included, default overlay with ID equal
to sending widget\u2019s ID is used. If an overlay exists, it will retain its status (whether visible or hidden). If an overlay is created, it
will be made visible.",
"type": "string"
},
"featureId": {
"description": "Unique identifier for the given feature data. Note that feature IDs MUST be unique within a given overlay.
Reusing a feature ID will be considered a reload, with the original feature data being removed and replaced by the new feature
data.",
"type": "string"
},
"name": {
"description": "Name for the given feature data. Note that feature names do not have to be unique and are intended for
display purposes only.",
"type": "string"
},

https://github.com/CMAPI/cmapi-1.2.0/blob/master/channels/map.feature.plot.js
| 220 |

Not
| 221 |

Heres what I dont want

The "not" keyword is used to specify what you don't want.


This schema says that JSON instances can contain any value,
as long as it's not a string:

{
"$schema": "http://json-schema.org/draft-04/schema#",
"not": { "type": "string" }
}

Here are two valid values: true ["Hello", "World"]

See example34

Do Lab13
| 222 |

Use case: identifier must not be a reserved


word

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
The value of an identifier "identifier": {
must not be any of these "type": "string",
reserved words: "maxLength": 20,
"pattern": "^[a-zA-Z][a-zA-Z0-9]*$",
"not": {"enum": ["if", "then", "else", "case", "let", "var"]}
}
}
}

Valid instance: Invalid instance:


{ {
"identifier": "X" "identifier": "if"
} }

See example34.1
| 223 |

Use case: ban properties


{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"length": { "type": "number", "minimum": 0 },
"width": { "type": "number", "minimum": 0 },
"radius": { "type": "number", "minimum": 0 },
"diameter": { "type": "number", "minimum": 0 }
},
"additionalProperties": false,
"allOf": [
Ban the presence of
{ "not": {"required": ["radius"]} },
radius and diameter { "not": {"required": ["diameter"]} }
in instances ]
}

Valid instance: Invalid instance:


{ {
"length": 10, "length": 10,
"width": 5 "radius": 9
} }
See example34.2
| 224 |

Repository of schemas
| 225 |

Creating a repository of schemas

The "definitions" keyword is used to define a repository of


schemas. The value of "definitions" is a JSON object which
contains one or more properties and the value of each property is
a schema:

"definitions": {
"property1": JSON Schema
"property2": JSON Schema

}
| 226 |

Repository with 2 schemas

{
"$schema": "http://json-schema.org/draft-04/schema#",
"definitions": {
"person":
{
"type": "object",
"properties": {
"name": { "type": "string", "maxLength": 20,"pattern": "^[a-zA-Z ]*$" },
"age": { "type": "number", "minimum": 0, "maximum": 120 } 2 schemas
},
"required": [ "name", "age" ],
"additionalProperties": false
},
"evens":
{
"type": "number", "multipleOf": 2
}
}
}

See example35
| 227 |

Referencing a schema

The "$ref" keyword is used to reference a schema:


"$ref": reference to a json-schema
The value of "$ref" is a path expression (very similar to path
expressions in XPath). A json-schema validator replaces "$ref"
and its value by the schema that it references.
| 228 |

Internal reference

The "$ref" in the following schema references the evens


schema.
The # symbol indicates an internal reference; specifically, it
references the schema within "evens" that is within
"definitions":
{
"$schema": "http://json-schema.org/draft-04/schema#",
"definitions": {
"person":
{
"type": "object",
"properties": {
"name": { "type": "string", "maxLength": 20,"pattern": "^[a-zA-Z ]*$" },
"age": { "type": "number", "minimum": 0, "maximum": 120 }
},
"required": [ "name", "age" ],
"additionalProperties": false
},
"evens":
{
"type": "number", "multipleOf": 2
}
},
"$ref": "#/definitions/evens" Do Lab14
}

See example36
| 229 |

Equivalent
{
"$schema": "http://json-schema.org/draft-04/schema#",
"definitions": {
"person":
{
"type": "object",
"properties": {
"name": { "type": "string", "maxLength": 20,"pattern": "^[a-zA-Z ]*$" },
"age": { "type": "number", "minimum": 0, "maximum": 120 }
},
"required": [ "name", "age" ],
"additionalProperties": false
},
"evens":
{
"type": "number", "multipleOf": 2
}
},
"$ref": "#/definitions/evens"
}

equivalent

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "number",
"multipleOf": 2
}
| 230 |

External reference

"$ref" can reference a schema in another document, as shown


here:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"$ref": "definitions.json#/definitions/evens"
}
| 231 |

Reference parent schema

"$ref" can reference any schema, including the schema it is


contained within:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"$ref": "#"
}

That schema is valid: it validates against the JSON schema-for-


schemas. However, when a JSON instance is validated against
that schema, an infinite loop error will be generated.

There are schemas which are perfectly


valid but have no conforming instances.
| 232 |

Schema-for-schema uses "definitions" and


"$ref"

{
"$schema": "http://json-schema.org/draft-04/schema,
"definitions": {

"stringArray": {
"type": "array",
"items": { "type": "string" },
"minItems": 1,
"uniqueItems": true
},

"required": { "$ref": "#/definitions/stringArray" },

}
| 233 |

Schema-for-schema references itself

{
"$schema": "http://json-schema.org/draft-04/schema,

"properties": {
"type": "object",
"additionalProperties": { "$ref": "#" },
"default": {}
},

}
| 234 |

"definitions" can be in a subschema

The "definitions" keyword does not have to be at the root schema


level. It can be embedded in a subschema and then referenced, as
shown here:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"properties": {
"name": { "type": "string", "maxLength": 20,"pattern": "^[a-zA-Z ]*$",
"definitions": {
"evens": { "type": "number", "multipleOf": 2 }
}
},
"age": { "type": "number", "minimum": 0, "maximum": 120 }
},
"$ref": "#/properties/name/definitions/evens"
}

See example37
| 235 |

Where is $ref defined in the specs?

$ref is defined in a separate document, JSON Reference.


The JSON Core specification just references it:
http://json-schema.org/latest/json-schema-core.html#anchor26

Here is the JSON Reference specification:


http://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03
| 236 |

How to reference these values

reference the first array value

{
"foo": ["bar", "baz"],
"": 0, reference the value of a property whose
" ": 7, name is the empty string
}

reference the value of a property whose name is a space


| 237 |

Heres how to reference the values

{
"foo": ["bar", "baz"],
"": 0,
" ": 7,
}

Path What is referenced


# the whole document
#/foo ["bar", "baz"]
#/foo/0 "bar"
#/ 0
#/%20 7
| 238 |

How to reference the first "Name" property?


{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Product",
"description": "A product from Acme's catalog",
"type": "object",
"properties": {
"Id": {
"description": "The unique identifier for a product",
"type": "integer"
},
"Name": { How to reference the value of
"description": "Name of the product", this "Name"?
"type": "string"
},
"Name": {"type": "integer"}, How to reference the
"Price": { value of this "Name"?
"type": "number",
"minimum": 0,
"maximum": 10,
"exclusiveMinimum": true
}
},
"additionalProperties": false
}
| 239 |

Dont use duplicate "Name" properties

Although technically it has been argued that JSON allows for


duplicate keys, it is discouraged and many (most?) JSON
parsers will either raise an error or drop one of the values. (Note
that I'm referring to JSON, not JSON Schema). As such, you
should avoid the duplicate "Name" properties in the first place.

Google "JSON duplicate keys" for some good discussions on the


topic.

Chris White

The possibility of having duplicate keys in a JSON object is


widely considered to be a bug in the JSON spec. Most parsers
do not support it (and will silently pick one value or another), and
in the discussion I've seen about updating the JSON standard,
cleaning up this issue by disallowing multiple properties is pretty
uncontroversial.

So basically, don't have multiple "Name" entries in your schema


object. :) This isn't just a JSON Pointer or JSON Schema thing,
but duplicate key names is a very bad idea in JSON data of any
sort.

Geraint David Luff


| 240 |

Multiple occurrences of a keyword

It is okay to have multiple occurrences of the same keyword if


they are in different schemas.
In the following schema one "maxLength" is in the root schema
and the other is in a subschema
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "string",
"maxLength": 5,
"allOf": [
{
"maxLength": 7
}
]
}
| 241 |

Multiple occurrences of a keyword

It is not okay to have multiple occurrences of the same keyword


if they are in the same schema.
This is not legal:
{
"type": "string",
"pattern": "^\([0-9]{3}\) [0-9]{3}-[0-9]{4}$",
"pattern": "^[0-9]{3}-[0-9]{3}-[0-9]{4}$"
}
| 242 |

oneOf
| 243 |

Choice of schemas

A choice of schemas can be created using the "oneOf" keyword.


The value of "oneOf" is an array and each item in the array must be
a schema:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"oneOf": [
JSON Schema,
JSON Schema,

]
}
| 244 |

Either a number or a boolean

This schema says that a JSON instance must contain either a


number or a boolean:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"oneOf": [
{"type": "number"},
{"type": boolean"}
]
}

Here are two valid values: 12 true


See example38

Do Lab15
| 245 |

Equivalent

{
"$schema": "http://json-schema.org/draft-04/schema#",
"oneOf": [
{"type": "number"},
{"type": boolean"}
]
}

equivalent

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": [ "boolean", "number" ]
}
| 246 |

A multiple of 3 or multiple of 5

This schema says that a JSON instance must contain a number


that is either a multiple of 3 or a multiple of 5:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "number",
"oneOf": [
{ "multipleOf": 3 },
{ "multipleOf": 5 }
]
}

Here are two valid values: 9 10


See example39
| 247 |

Content of "oneOf" are schemas?

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "number",
"oneOf": [
{"multipleOf": 3},
{"multipleOf": 5}
]
}

Hold on, those


arent schemas
| 248 |

Heres how "oneOf" really works

{
"$schema": "http://json-schema.org/draft-04/schema#",
A,
"oneOf": [
The base schema. { B },
The instance must {C}
validate against the ],
D
base schema.
}
| 249 |

Heres how "oneOf" really works

{
"$schema": "http://json-schema.org/draft-04/schema#",
A,
"oneOf": [
The subschemas. { B },
The instance must {C}
validate against one ],
D
of the subschemas.
}
| 250 |

Heres how "oneOf" really works

{
"$schema": "http://json-schema.org/draft-04/schema#",
A,
"oneOf": [
{ B },
{C}
],
D
}

The instance must validate against the base


schema and one of the subschemas.
| 251 |

One of the choices can be the empty schema

The following schema says that an instance value must be either


anything or a number:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"oneOf": [
{},
{"type": "number"}
]
}

Here are two valid values: true [ "Hello", "World" ]

See example40
| 252 |

Is this a valid value?

{
"$schema": "http://json-schema.org/draft-04/schema#",
"oneOf": [
{},
{"type": "number"}
]
}

Is this a valid value? 12


| 253 |

No!

12 is an invalid instance because it matches both schemas


listed in "oneOf"
An instance must validate against exactly one of the schemas
listed in "oneOf"
| 254 |

One of the choices refs the parent schema

The following schema is a valid schema:


{
"$schema": "http://json-schema.org/draftv4/schema#",
"oneOf": [
{"type": "number"},
{ "$ref": "#" }
]
}

However, when an instance document is validated against the


schema, the validator will throw an infinite-loop error.

See example41
| 255 |

Ref to schemas within "definitions"

This schema says that a JSON instance must contain either a


person object or an even number:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"definitions": {
"person": {
"type": "object",
"properties": {
"name": { "type": "string", "maxLength": 20,"pattern": "^[a-zA-Z ]*$" },
"age": { "type": "number", "minimum": 0, "maximum": 120 }
},
"required": [ "name", "age" ],
"additionalProperties": false
},
"evens": { "type": "number", "multipleOf": 2}
},
"oneOf": [
{"$ref": "#/definitions/person"},
{"$ref": "#/definitions/evens"}
]
}
See example42
| 256 |

Schema-valid values

Here are two values that validate against the schema on the
previous slide:
12

{
"name": "John Doe",
"age": 30
}
| 257 |

Choose between two objects with different


properties
What does the following JSON schema mean?
{
"$schema": "http://json-schema.org/draft-04/schema#",
"oneOf": [
{
"type": "object",
"properties": {"A": {"type": "string"}}
},
{
"type": "object",
"properties": {"B": {"type": "string"}}
}
]
}

An instance must be either an object with a property A or an


object with a property B. Is that what it means?
| 258 |

No valid instance!

The following instance matches both alternatives of "oneOf"


{
"A": "Hello"
}

The JSON schema validator examines the instance document to


see if it matches the type of exactly one alternative of "oneOf"
Both alternatives of "oneOf" specify that the instance must be of
type object so there can never be an instance that matches only
one of the alternatives of "oneOf"
| 259 |

Here's how to implement it

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"A": {"type": "string"},
"B": {"type": "string"}
},
"oneOf": [
{"required": ["A"]}, The object must contain one of these
{"required": ["B"]}
]
}
| 260 |

Create a schema for this problem

Instances must contain this property: "common"


Instances must contain either an "a" property or a "b" property
So, instances must contain "common" and "a" or "common"
and "b"
Instances must not contain anything else.
Create the schema for this.

"common":
"a":
choice
"b":
| 261 |

Heres the schema

{
"type": "object",
"properties": {
"common": { "type": "string" },
"a": { "type": "integer" },
"b": { "type": "integer" }
},
"required":["common"],
"additionalProperties": false,
"oneOf":
[
{ "required": ["a"] },
{ "required": ["b"] }
]
}

See example43
| 262 |

allOf
| 263 |

Value of the "allOf" keyword

The value of the "allOf" keyword is an array.


Each item in the array is a schema.

{
"$schema": "http://json-schema.org/draft-04/schema#",
"allOf": [
JSON Schema,
JSON Schema,

]
}
| 264 |

The "allOf" keyword

An instance must validate against the base schema and it must


validate against each schema specified within "allOf".
Example: the schema on the next slide says that an instance
must conform to the "person" schema and it must have a
"height" property.
| 265 |

Instance must conform to 2 subschemas


{
"$schema": "http://json-schema.org/draft-04/schema#",
"definitions": {
"person": {
"type": "object",
"properties": {
"name": { "type": "string", "maxLength": 20,"pattern": "^[a-zA-Z ]*$" },
"age": { "type": "number", "minimum": 0, "maximum": 120 }
},
"required": [ "name", "age" ]
},
"evens": { "type": "number", "multipleOf": 2}
},
"allOf": [
{"$ref": "#/definitions/person"}, Instances must
{ conform to these
"type": "object",
2 subschemas
"properties": {
"height": { "type": "number", "minimum": 0 }
},
"required": [ "height" ]
}
],
"maxProperties": 3
}
See example44
| 266 |

Valid instance
{
"$schema": "http://json-schema.org/draft-04/schema#",
"definitions": {
"person": {
"type": "object",
"properties": {
"name": { "type": "string", "maxLength": 20,"pattern": "^[a-zA-Z ]*$" },
"age": { "type": "number", "minimum": 0, "maximum": 120 }
},
"required": [ "name", "age" ]
},
"evens": { "type": "number", "multipleOf": 2}
},
"allOf": [
{"$ref": "#/definitions/person"},
{
"type": "object",
"properties": { {
"height": { "type": "number", "minimum": 0 }
}, "name": "John Doe",
"required": [ "height" ]
} "age": 30,
],
"maxProperties": 3
"height": 68
} }

JSON Schema
Validator

valid!
| 267 |

First, validate against the base schema


{
"$schema": "http://json-schema.org/draft-04/schema#",
"definitions": {
"person": {
"type": "object",
"properties": {
"name": { "type": "string", "maxLength": 20,"pattern": "^[a-zA-Z ]*$" },
"age": { "type": "number", "minimum": 0, "maximum": 120 }
},
"required": [ "name", "age" ]
},
"evens": { "type": "number", "multipleOf": 2}
},
"allOf": [
{"$ref": "#/definitions/person"},
{
"type": "object",
"properties": { {
"height": { "type": "number", "minimum": 0 }
}, "name": "John Doe",
"required": [ "height" ]
} "age": 30,
],
"maxProperties": 3
"height": 68
} }

JSON Schema
Validator

valid!
| 268 |

Second, validate against the first subschema


{
"$schema": "http://json-schema.org/draft-04/schema#",
"definitions": {
"person": {
"type": "object",
"properties": {
"name": { "type": "string", "maxLength": 20,"pattern": "^[a-zA-Z ]*$" },
"age": { "type": "number", "minimum": 0, "maximum": 120 }
},
"required": [ "name", "age" ]
},
"evens": { "type": "number", "multipleOf": 2}
},
"allOf": [
{"$ref": "#/definitions/person"},
{
"type": "object",
"properties": { {
"height": { "type": "number", "minimum": 0 }
}, "name": "John Doe",
"required": [ "height" ]
} "age": 30,
],
"maxProperties": 3
"height": 68
} }

JSON Schema
Validator

valid!
| 269 |

Third, validate against the second


subschema
{
"$schema": "http://json-schema.org/draft-04/schema#",
"definitions": {
"person": {
"type": "object",
"properties": {
"name": { "type": "string", "maxLength": 20,"pattern": "^[a-zA-Z ]*$" },
"age": { "type": "number", "minimum": 0, "maximum": 120 }
},
"required": [ "name", "age" ]
},
"evens": { "type": "number", "multipleOf": 2}
},
"allOf": [
{"$ref": "#/definitions/person"},
{
"type": "object",
"properties": { {
"height": { "type": "number", "minimum": 0 }
}, "name": "John Doe",
"required": [ "height" ]
} "age": 30,
],
"maxProperties": 3
"height": 68
} }

JSON Schema
Validator

valid!
| 270 |

Instance is valid only if all 3 validations return


valid

valid instance = valid base schema AND


valid subschema1 AND
valid subschema2

Do Lab16
| 271 |

No valid instances

This "allOf" lists two subschemas, one specifies that the instance
must be a number while the other specifies that the instance must
be a boolean:
{
"allOf": [
{ "type": "number" },
{ "type": "boolean" }
]
}

No instance can conform to both subschemas since the schema


requires the value be both a number and a boolean, which is
impossible.
| 272 |

What instances validate against this schema?

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type" : "object",
"allOf" : [
{ "properties" : {"last_name" : { "type" : "string" }}, "required": ["last_name"] },
{ "properties" : {"first_name" : { "type" : "string" }}, "required": ["first_name"] }
],
"additionalProperties": false
}

See example45
| 273 |

The "allOf" keyword

Here is the base schema:


{
"$schema": "http://json-schema.org/draft-04/schema#",
"type" : "object",
"allOf" : [
{ "properties" : {"last_name" : { "type" : "string" }}, "required": ["last_name"] },
{ "properties" : {"first_name" : { "type" : "string" }}, "required": ["first_name"] }
],
"additionalProperties": false
}

Only the empty instance { } validates against the base schema.


| 274 |

Instances must validate against 3 schemas

Here are the two subschemas defined within "allOf":


{ "properties" : {"last_name" : { "type" : "string" }}, "required": ["last_name"] }

{ "properties" : {"first_name" : { "type" : "string" }}, "required": ["first_name"] }

An instance must validate against the base schema plus the two
subschemas.
| 275 |

This instance doesnt validate

Consider this instance:


{
"last_name": "Doe",
"first_name": "John"
}

Validating that instance yields the error: "last_name and


first_name are not allowed by the schema."
That's because the base schema does not allow last_name and
first_name.
| 276 |

And this instance doesnt validate

Consider this instance :


{}
then the instance validates against the base schema but not the
subschemas. Validation yields these errors: "object has missing
required properties "last_name" and "object has missing required
properties "first_name".
| 277 |

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type" : "object",
"allOf" : [
{ "properties" : {"last_name" : { "type" : "string" }}, "required": ["last_name"] },
{ "properties" : {"first_name" : { "type" : "string" }}, "required": ["first_name"] }
],
"additionalProperties": false
}

This schema has no valid instances!


| 278 |

Multiple occurrences of the same keyword,


which one wins?
This schema has two "maxLength" keywords:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "string",
"maxLength": 5,
"allOf": [
{
"maxLength": 7
}
]
}

Whats the maximum length of a string: 5 or 7?

See example45.1
| 279 |

All constraints must be met

Both "maxLength" constraints must be met.


The "maxLength": 5 is part of the base schema, so any data will
be validated against that first. If that holds true, it will then
proceed to "allOf", and the data must also validate against every
schema in the allOf array.
Good value:
"12345"
Bad value:
"123456" (too long)
| 280 |

anyOf
| 281 |

Value of the "anyOf" keyword

The value of the "anyOf" keyword is an array.


Each item in the array is a schema.
{
"$schema": "http://json-schema.org/draft-04/schema#",
"anyOf": [
JSON Schema,
JSON Schema,

]
}
| 282 |

Semantics of the "anyOf" keyword

An instance validates successfully against the "anyOf" keyword if


it validates successfully against at least one schema defined by
this keyword's value.
| 283 |

Example schema using "anyOf"

The following schema says that JSON instances can be either a


number or a boolean:
{
"$schema": "http://json-schema.org/draftv4/schema#",
"anyOf": [
{"type": "number"},
{"type": "boolean"}
]
}
Here are two valid values: 12 false
| 284 |

"oneOf " versus "anyOf"


{
"$schema": "http://json-schema.org/draft-04/schema#",
"oneOf": [
{},
{"type": "number"}
]
} 12

valid

JSON Schema
JSON Schema Validator
Validator

invalid
12 {
"$schema": "http://json-schema.org/draft-04/schema#",
anyOf": [
{},
{"type": "number"}
See example46 ]
}
| 285 |

"anyOf" specifies required properties

The "anyOf" in the following schema says that JSON instances


must contain a "name" property, an "age" property, or both. And
nothing else:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"name": { "type": "string", "maxLength": 20,"pattern": "^[a-zA-Z ]*$" },
"age": { "type": "number", "minimum": 0, "maximum": 120 }
},
"anyOf": [
{"required": [ "name" ]},
{"required": [ "age" ]}
],
"additionalProperties": false
}

See example47
| 286 |

Difference between these schemas?


{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"name": { "type": "string", "maxLength": 20,"pattern": "^[a-zA-Z ]*$" },
"age": { "type": "number", "minimum": 0, "maximum": 120 }
}
"additionalProperties": false
}

difference?

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"name": { "type": "string", "maxLength": 20,"pattern": "^[a-zA-Z ]*$" },
"age": { "type": "number", "minimum": 0, "maximum": 120 }
},
"anyOf": [
{"required": [ "name" ]},
{"required": [ "age" ]}
],
"additionalProperties": false
}
| 287 |

The difference is with an empty instance

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object", The empty object { }
"properties": {
"name": { "type": "string", "maxLength": 20,"pattern": "^[a-zA-Z ]*$" },
is schema-valid
"age": { "type": "number", "minimum": 0, "maximum": 120 }
}
"additionalProperties": false
}

{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"name": { "type": "string", "maxLength": 20,"pattern": "^[a-zA-Z ]*$" }, The empty object { }
"age": { "type": "number", "minimum": 0, "maximum": 120 } is not schema-valid
},
"anyOf": [
{"required": [ "name" ]},
{"required": [ "age" ]}
],
"additionalProperties": false
}
| 288 |

Each subschema must be processable

All subschemas within "anyOf" must be processable by the


validator. Thus, instances will fail validation against the following
schema because the second subschema within "anyOf" contains
an infinite loop (which is not processable):

{
"$schema": "http://json-schema.org/draftv4/schema#",
"anyOf": [
{"type": "number"},
{ "$ref": "#" }
]
}

See example48
| 289 |

default
| 290 |

The "default" keyword

The "default" keyword is used to provide a default value.


The following schema creates an object that has the properties:
"title", "name", and "age".
The allowable values for "title" is specified using "enum". If a
JSON instance does not contain a "title" the value "Mr" is the
default.

{
"$schema": "http://json-schema.org/draft-04/schema#",
default value for "title"
"type": "object",
"properties": {
"title": { "type": "string", "enum": ["Mr", "Mrs", "Miss"], "default": "Mr"},
"name": { "type": "string", "maxLength": 20,"pattern": "^[a-zA-Z ]*$" },
"age": { "type": "number", "minimum": 0, "maximum": 120 }
},
"required": [ "name", "age" ],
"additionalProperties": false
}
See example49
| 291 |

"default" is ignored by validators

The value of "default" is ignored by schema validators. So this is a


legal default:

"title": { "type": "string", "enum": ["Mr", "Mrs", "Miss"], "default": "BlahBlah"}


| 292 |

Expressing co-constraints
| 293 |

Expressing co-constraints

The "dependencies" keyword enables constraints between


properties (a.k.a. co-constraints) to be expressed.
Consider properties of these shapes: circle and rectangle. Data
for radius and diameter must be provided for circle. Data for
width and length must be provided for rectangle. So if there is a
radius, there must be a diameter and vice versa. If there is a
width, there must be a length and vice versa.
The schema on the next slide specifies that radius/diameter and
length/width must always occur in pairs.
| 294 |

Co-constraint: If there is a radius, then there


must be a diameter and vice versa
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"length": { "type": "number", "minimum": 0 },
"width": { "type": "number", "minimum": 0 },
"radius": { "type": "number", "minimum": 0 },
"diameter": { "type": "number", "minimum": 0 }
},
"dependencies": {
"radius": ["diameter"], If the instance has "radius" then
"diameter": ["radius"], it must also have "diameter"
"length": ["width"], and vice versa.
"width": ["length"]
}, If the instance has length" then
"additionalProperties": false it must also have width" and
}
vice versa.

See example50
| 295 |

Properties must occur in pairs, or its invalid

Here is a valid instance:


{
"length": 10,
"width": 20
}

The following instance is invalid because it has a "length" without


the required "width" property:
{
"length": 10
}
| 296 |

Multiple pairs are okay

Note that "dependencies" does not prohibit other properties from


appearing in instances. For example, this is a valid instance:
{
"length": 10,
"width": 5,
"radius": 10,
"diameter": 10
}
| 297 |

An array list of dependent properties

The value of "dependencies" is an object; the value of each object


property is an array of strings. The array may contain strings that
are not the name of any property, such as this:

"radius": ["diameter", "Blah"]

If "additionalProperties": false is specified, then "Blah" will not be


allowed in instances and there is a paradox: the schema mandates
that the instance contain "Blah" whenever it contains "radius" but
"additionalProperties": false disallows instances to contain
"Blah".
| 298 |

No co-constraint between property and value

"dependencies" cannot be used to express co-constraints


between properties and values.
For example, consider a schema with an object that has a
"shape" property and an "enum" with values "circle" or
"rectangle":
{ It would be useful to express this:
"$schema": "http://json-schema.org/draft-04/schema#", If the value of "shape" is "circle"
"type": "object", then the "radius" and "diameter"
"properties": { properties must be present.
"shape": { "type": "string", "enum": ["circle", "rectangle"] }, If the value of "shape" is
"length": { "type": "number", "minimum": 0 }, "rectangle" then the "width" and
"width": { "type": "number", "minimum": 0 }, "length" properties must be
"radius": { "type": "number", "minimum": 0 }, present.
"diameter": { "type": "number", "minimum": 0 } However, that capability is not
}, provided by JSON Schema.
"additionalProperties": false
}
| 299 |

Property versus schema dependency

We have seen that within "dependencies" you specify a property


with an array value and each item in the array is the name of
another property. This is called a property dependency.
There is another way to use "dependencies": you specify a
property and the value of it is an object that defines additional
properties. This is called a schema dependency.
| 300 |

Example of a schema dependency

The schema on the following slide says:


If the instance contains the "car" property then the instance must
also contain a "numPassengers" property.
If the instance contains the "juicers" property then the instance
must also contain a "kind" property.
| 301 |

Schema dependency
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"car": { "type": "string", "maxLength": 20 },
"juicer": { "type": "string", "maxLength": 20 }
},
"dependencies": {
"car": {
"type": "object",
"properties": { If "car" then "numPassengers"
"numPassengers": { "type": "integer", "minimum": 1 }
},
"required": ["numPassengers"]
},
"juicer": {
"type": "object",
"properties": { If "juicer" then "kind"
"kind": { "type": "string", "enum": ["press", "centrifugal"] }
},
"required": [kind"]
}
}
}
See example51
| 302 |

Schema-valid instance
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"car": { "type": "string", "maxLength": 20 },
"juicer": { "type": "string", "maxLength": 20 }
},
"dependencies": {
"car": {
"type": "object",
"properties": {
"numPassengers": { "type": "integer", "minimum": 1 }
},
"required": ["numPassengers"]
},
"juicer": {
"type": "object",
"properties": {
"kind": { "type": "string", "enum": ["press", "centrifugal"] }
}, {
"required": [kind"]
}
"car": "Toyota Avalon",
}
} "numPassengers": 5
}

JSON Schema
Validator

valid!
| 303 |

An instance could also contain "juicer" and


its dependent "kind" property
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"car": { "type": "string", "maxLength": 20 },
"juicer": { "type": "string", "maxLength": 20 }
},
"dependencies": {
"car": {
"type": "object",
"properties": {
"numPassengers": { "type": "integer", "minimum": 1 }
},
"required": ["numPassengers"]
},
"juicer": {
"type": "object",
{
"properties": { "car": "Toyota Avalon",
"kind": { "type": "string", "enum": ["press", "centrifugal"] }
}, "numPassengers": 5,
"required": [kind"]
} "juicer": "Omega Juicer",
}
} "kind": "centrifugal"
}

JSON Schema
Validator

valid!
| 304 |

Constrain the number of properties

If we want to allow just "car" (and its dependent property,


"numPassenger") or "juicer" (and its dependent propert, "kind")
but not both, use "maxProperties": 2 as shown here:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"car": { "type": "string", "maxLength": 20 },
"juicer": { "type": "string", "maxLength": 20 }
},
"maxProperties": 2, Instances can contain no
"dependencies": {
"car": { more than 2 properties.
"type": "object",
"properties": {
"numPassengers": { "type": "integer", "minimum": 1 }
},
"required": ["numPassengers"]
},
"juicer": {
"type": "object",
"properties": {
"kind": { "type": "string", "enum": ["press", "centrifugal"] }
},
"required": [kind"]
}
}
}
| 305 |

Recursion
| 306 |

Create a schema for this

{
"book": {
"title": "Title 1",
"section": {
"title": "Title 1.1",
"section": {
A section contains a title and a section.
"title": "Title 1.1.1", See the recursion?
"section": {
"title": "Title 1.1.1.1"
}
}
}
}
}
| 307 |

Heres what we want

title: string
section:
| 308 |

Create a recursive "definitions"

"definitions": {
"section": {
"type": "object",
"properties": {
"title": { "type": "string" },
"section": { "$ref": "#/definitions/section" }
},
"required": [ "title" ],
"additionalProperties": false
}
},
| 309 |

Create "book" and set its value to point to the


recursive schema

"definitions": {
"section": {
"type": "object",
"properties": {
"title": { "type": "string" },
"section": { "$ref": "#/definitions/section" }
},
"required": [ "title" ],
"additionalProperties": false
}
},

"properties": {
"book": { "$ref": "#/definitions/section" }
}
| 310 |

Heres the schema

{
"$schema": "http://json-schema.org/draft-04/schema#",
"definitions": {
"section": {
"type": "object",
"properties": {
"title": { "type": "string" },
"section": { "$ref": "#/definitions/section" }
},
"required": [ "title" ],
"additionalProperties": false
}
},
"type": "object",
"properties": {
"book": { "$ref": "#/definitions/section" }
}
}
See example52
| 311 |

Unbounded nesting

The recursive schema on the previous slide allows unbounded


nesting.
There is no way to limit the amount of nesting or recursion.
The End