If you specify elements in the input file that is configured as a requirement in the schema, it is validating OK. And if you append the “maxItems”: 1, it does not care if you add another element in the input file, the validator still look at this as a valid input file.
i.e: Schema:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"Books": {
"type": "object",
"minItems": 1,
"properties": {
"Book": {
"type": "object",
"minItems": 1,
"maxItems": 1,
"properties": {
"Author": {
"type": "string",
"minItems": 1,
"maxItems": 1
}
},
"required": ["Author"]
}
},
"required": ["Book"]
}
},
"required": ["Books"]
}
InputFile:
{
"Books": {
"Book": {
"Author": "Andreas",
"Author": "Geir"
}
}
}
Shouldn't this be a invalid inputfile?
The validators:
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
Author (string)
and TotalPages (integer)
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"]
}
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:
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)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"
}
]
}
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: