According to your defined schema, the given JSON is correct.
What your schema is saying is that for each object Author
, there should be a minimum of 1 and maximum of 1 string property, which your JSON conforms to.
In addition to that, the properties minItems
and maxItems
are specifically for arrays, but in your definition they are under objects. Read more about that in the linked documentation at the bottom.
The part where confusion is coming in is that you are expecting arrays to be objects and objects to be arrays, which can sometimes be hard to distinguish.
In very simple terms:
A JSON object is a set of key:value pairs. It would be as if you are defining an object (class) and setting it's properties values in an OOP language.
A basic definition of a JSON object:
{
"type": "object",
"properties": {
"MyString": {
"type": "string"
},
"MyInterger": {
"type": "integer"
}
}
A JSON array is a collection of the same, sometimes similar, objects or single values.
A basic definition of a JSON array:
{
"type": "array",
"items": {
"type": "string"
}
}
What also can help to define what to use when, is to think of what you want to create, but as an object in an OOP language.
Example:
For the following Book
JSON object, I could imagine a class structure as shown and then subsequently create the schema from that:
JSON:
{
"Author": "First Author",
"TotalPages": 128,
"Chapters": [
{
"Number": 1,
"Heading": "Chapter One"
},
{
"Number": 2,
"Heading": "Chapter Two"
}
]
}
What we have are the
- Two basic objects
Author (string)
and TotalPages (integer)
- An array of the
Chapters
object, which contains two basic objects Number (integer)
and Heading (string)
Class representation:
public class Book
{
public string Author { get; set; }
public int TotalPages { get; set; }
// Note the array
public Chapter[] Chapters { get; set; } // Could be List<Chapter>
}
public class Chapter
{
public int Number { get; set; }
public string Heading { get; set; }
}
The resulting Schema:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"Author": {
"type": "string"
},
"TotalPages": {
"type": "integer"
},
"Chapters": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"properties": {
"Number": {
"type": "integer"
},
"Heading": {
"type": "string"
}
}
}
}
},
"required": ["Author", "Chapters"]
}
Now you will notice I purposely left out the `Books` section, since that is *an array/collection* of `Book`. If we want to add that to the JSON schema and class, we will need to define it as such. While we are at it, let us also add a string array per book for `Keywords`, just so it is clear how each can be defined.
Firstly lets change our desired output (the JSON).
We want our base object to now be Books
and it should be a collection of the Book
object, so we enclose the Book
object in [ ]
and for the heck of it add another book. We also add a collection of Keywords
to the object Book
.
{
"Books":
[
{
"Author": "First Author",
"TotalPages": 128,
"Chapters": [
{
"Number": 1,
"Heading": "Chapter One"
},
{
"Number": 2,
"Heading": "Chapter Two"
}
],
"Keywords": [
"This",
"is",
"book",
"Alpha"
]
},
{
"Author": "Second Author",
"TotalPages": 256,
"Chapters": [
{
"Number": 1,
"Heading": "Erstes Kapitel"
},
{
"Number": 2,
"Heading": "Zweites Kapitel"
}
],
"Keywords": [
"This",
"is just",
"Beta"
]
}
]
}
Now we have the following:
- An object
Books
that contains an array of our previously defined Book
object. (Note that Book
is never named since that would add another level of hierarchy into the JSON)
- In addition to our previously defined objects, we also have an array of
string
representing the Keywords
Let us change our class/object representation of our JSON, doing this will help with knowing how to modifying the schema.
public class MyBookCollection
{
// Note the array!!
public Book[] Books { get; set; } // Could also be List<Book>
}
public class Book
{
public string Author { get; set; }
public int TotalPages { get; set; }
// Note the arrays!!
public Chapter[] Chapters { get; set; } // Could also be List<Chapter>
public string[] Keywords { get; set; } // Could also be List<string>
}
public class Chapter
{
public int Number { get; set; }
public string Heading { get; set; }
}
We know now what our data and class will look like when we eventually get to parse the JSON. Lets change the JSON Schema so that we have something that we can use in the validators.
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"Books": {
"type": "array",
"minItems": 1,
"maxItems": 15,
"title": "Book",
"items": {
"type": "object",
"properties": {
"Author": {
"type": "string"
},
"TotalPages": {
"type": "integer"
},
"Chapters": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"properties": {
"Number": {
"type": "integer"
},
"Heading": {
"type": "string"
}
}
}
},
"Keywords": {
"type": "array",
"minItems":2,
"items": {
"type": "string"
}
}
},
"required": ["Author", "Chapters"]
}
}
}
}
I added some minItems
and maxItems
to the array definitions so that you can see where and how to set them. You can copy the schema and data to any of the validators and play around with them to see how they work.
One additional important thing:
You cannot prevent or check for duplicate properties inside an object by means of a Schema validation.
As an example, using our simple JSON object and adding a duplicate property,
{
"Author": "First Author",
"Author": "!!Duplicate Author!!",
"TotalPages": 128,
"Chapters": [
{
"Number": 1,
"Heading": "Chapter One"
},
{
"Number": 2,
"Heading": "Chapter Two"
}
]
}
dumping that JSON into any of the validators mentioned, they will all validate as **correct** and **passed**. I checked and this is confirmed on the [JSON Schema Google Group][6] that it cannot be checked for currently via a schema definition.
How the duplicate properties are handled is also library specific.
For example both the Newtonsoft.Json and ServiceStack libraries for C# will use the last occurrence of a property.
So from our example, the value of the Book.Author
property after deserialization with either library, would be "!!Duplicate Author!!".
Some sources: