Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JSON schema : "allof" with "additionalProperties"

Suppose we have schema following schema (from tutorial here):

{   "$schema": "http://json-schema.org/draft-04/schema#",    "definitions": {     "address": {       "type": "object",       "properties": {         "street_address": { "type": "string" },         "city":           { "type": "string" },         "state":          { "type": "string" }       },       "required": ["street_address", "city", "state"]     }   },    "type": "object",    "properties": {     "billing_address": { "$ref": "#/definitions/address" },     "shipping_address": {       "allOf": [         { "$ref": "#/definitions/address" },         { "properties":           { "type": { "enum": [ "residential", "business" ] } },           "required": ["type"]         }       ]     }     } } 

And here is valid instance:

{       "shipping_address": {         "street_address": "1600 Pennsylvania Avenue NW",         "city": "Washington",         "state": "DC",         "type": "business"       } } 

I need to ensure that any additional fields for shipping_address will be invalid. I know for this purpose exists additionalProperties which should be set to "false". But when I'm setting "additionalProprties":false as in the following:

"shipping_address": {           "allOf": [             { "$ref": "#/definitions/address" },             { "properties":               { "type": { "enum": [ "residential", "business" ] } },               "required": ["type"]             }           ],           "additionalProperties":false         }  

I get a validation error (checked here):

[ {   "level" : "error",   "schema" : {     "loadingURI" : "#",     "pointer" : "/properties/shipping_address"   },   "instance" : {     "pointer" : "/shipping_address"   },   "domain" : "validation",   "keyword" : "additionalProperties",   "message" : "additional properties are not allowed",   "unwanted" : [ "city", "state", "street_address", "type" ] } ]  

The question is: how should I to limit fields for the shipping_address part only? Thanks in advance.

like image 499
lm. Avatar asked Mar 27 '14 13:03

lm.


People also ask

What does allOf mean in JSON schema?

By the definition of this keyword, it meant that the instance had to be valid against the current schema and all schemas specified in extends ; basically, draft v4's allOf is draft v3's extends .

What is JSON schema additionalProperties?

The additionalProperties keyword is used to control the handling of extra stuff, that is, properties whose names are not listed in the properties keyword or match any of the regular expressions in the patternProperties keyword. By default any additional properties are allowed.

How do you reference a JSON schema?

In a JSON schema, a $ref keyword is a JSON Pointer to a schema, or a type or property in a schema. A JSON pointer takes the form of A # B in which: A is the relative path from the current schema to a target schema. If A is empty, the reference is to a type or property in the same schema, an in-schema reference.

What is required in JSON schema?

JSON Schema Example A little description of the schema. The type keyword defines the first constraint on our JSON data: it has to be a JSON Object. Defines various keys and their value types, minimum and maximum values to be used in JSON file. This keeps a list of required properties.


1 Answers

[author of the draft v4 validation spec here]

You have stumbled upon the most common problem in JSON Schema, that is, its fundamental inability to do inheritance as users expect; but at the same time it is one of its core features.

When you do:

"allOf": [ { "schema1": "here" }, { "schema2": "here" } ] 

schema1 and schema2 have no knowledge of one another; they are evaluated in their own context.

In your scenario, which many, many people encounter, you expect that properties defined in schema1 will be known to schema2; but this is not the case and will never be.

This problem is why I have made these two proposals for draft v5:

  • strictProperties,
  • merge.

Your schema for shipping_address would then be:

{     "merge": {         "source": { "$ref": "#/definitions/address" },         "with": {             "properties": {                 "type": { "enum": [ "residential", "business" ] }             }         }     } } 

along with defining strictProperties to true in address.


Incidentally, I am also the author of the website you are referring to.

Now, let me backtrack to draft v3. Draft v3 did define extends, and its value was either of a schema or an array of schemas. By the definition of this keyword, it meant that the instance had to be valid against the current schema and all schemas specified in extends; basically, draft v4's allOf is draft v3's extends.

Consider this (draft v3):

{     "extends": { "type": "null" },     "type": "string" } 

And now, that:

{     "allOf": [ { "type": "string" }, { "type": "null" } ] } 

They are the same. Or maybe that?

{     "anyOf": [ { "type": "string" }, { "type": "null" } ] } 

Or that?

{     "oneOf": [ { "type": "string" }, { "type": "null" } ] } 

All in all, this means that extends in draft v3 never really did what people expected it to do. With draft v4, *Of keywords are clearly defined.

But the problem you have is the most commonly encountered problem, by far. Hence my proposals which would quench this source of misunderstanding once and for all!

like image 96
fge Avatar answered Sep 19 '22 15:09

fge