jsonvalidationschemajsonschemajson-schema-validator

Validating JSON input with a JSON schema


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:


Solution

  • 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

    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:

    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: