JSON Schema away to specify an "any"-type schema with certain required fields

Bernardo Cunha picture Bernardo Cunha · Jan 27, 2013 · Viewed 17.7k times · Source

Let's say I have the following JSON Schema

{
 "name":"Product",
 "type":"object",
 "properties":{
   "id":{
     "type":"number",
     "required":true
   },
   "name":{
     "description":"Name of the product",
     "required":true
   },
   "price":{
     "required":true,
     "type": "number",
     "minimum":0,
     "required":true
   },
   "tags":{
     "type":"array",
     "items":{
       "type":"any"
     }
   }
 }
}

But, instead of tags being an array, I'd like it to be part of the root schema. So you could specify any property, but I give special attention to "id", "name" and "price" Which of the following would be the correct way of doing it, and which ones are completely wrong?

{
 "name":"Product",
 "type":"object",
 "properties":{
   "id":{
     "type":"number",
     "required":true
   },
   "name":{
     "description":"Name of the product",
     "required":true
   },
   "price":{
     "required":true,
     "type": "number",
     "minimum":0,
     "required":true
   }
 },
 "additionalProperties": {
     "type":"any"
 }
}

{
 "name":"Product",
 "type":"object",
 "properties":{
   "id":{
     "type":"number",
     "required":true
   },
   "name":{
     "description":"Name of the product",
     "required":true
   },
   "price":{
     "required":true,
     "type": "number",
     "minimum":0,
     "required":true
   }
 },
 "extends": {
     "type":"any"
 }
}

{
 "name":"Product",
 "type":["object","any"],
 "properties":{
   "id":{
     "type":"number",
     "required":true
   },
   "name":{
     "description":"Name of the product",
     "required":true
   },
   "price":{
     "required":true,
     "type": "number",
     "minimum":0,
     "required":true
   }
 }
}

I can come up with a few more (such as inverting roles of "any" and "object"), but they are all derivative of these three examples.

Answer

fge picture fge · Jan 28, 2013

[disclaimer: author of the next JSON Schema validation spec here]

OK, it is unclear what you ask, see below, but one of your examples clearly does not do what you want, this is the one where you write:

{ "type": [ "object", "any" ] }

This is equivalent to an empty schema, and as such validates each and every instance.

One way I read your question is that you want your JSON data to be either a tag array or an object with at least members named id, name and price. Since you seem to be using draft v3, you only have one solution:

{
    "type": [
        {
            "description": "schema for tags array here",
        },
        {
            "description": "schema for the base object here"
        }
    ]
}

This construct means that the instance must obey at least one schema inside type (and you can mix it with primitive types as well).

However: the current draft now being v4, you should now write:

{
    "anyOf": [
        {
            "description": "schema for tags array here",
        },
        {
            "description": "schema for the base object here"
        }
    ]
}

Note that not all implementations support draft v4, or the construct for type above. In fact, very few do. See below for a link to an online schema validator which supports both.

Another way that I read your question is that you want to allow properties other than id, name and price to be whatever they like. This is then quite simple. Just don't define a schema for tags in properties:

{
    "type": "object",
    "required": [ "id", "name", "price" ]
    "properties": {
        "id": {
            "type": "number"
        },
        "name": {
            "description": "Name of the product"
        },
        "price": {
            "type": "number",
            "minimum": 0
        }
    }
}

Since you don't specify additionalProperties as being false, the object instance can have any number of additional members, and these members can be anything.

Link to an online validator which can test your schemas: here