Interpretation of JSON Schema to CommonModel

The library transforms JSON Schema from data validation rules to data definitions (CommonModel(s)).

The algorithm tries to get to a model whose data can be validated against the JSON schema document.

As of now we only provide the underlying structure of the schema file for the model, where constraints/annotations such as maxItems, uniqueItems, multipleOf, etc. are not interpreted.

Patterns

Beside the regular interpreter we also look for certain patterns that are interpreted slightly differently.

oneOf with allOf

If both oneOf and allOf is present, each allOf model is merged into the interpreted oneOf.

For example take this example:

1{
2  "allOf":[
3    {
4      "title":"Animal",
5      "type":"object",
6      "properties":{
7        "animalType":{
8          "title":"Animal Type",
9          "type":"string"
10        },
11        "age":{
12          "type":"integer",
13          "min":0
14        }
15      }
16    }
17  ],
18  "oneOf":[
19    {
20      "title":"Cat",
21      "type":"object",
22      "properties":{
23        "animalType":{
24          "const":"Cat"
25        },
26        "huntingSkill":{
27          "title":"Hunting Skill",
28          "type":"string",
29          "enum":[
30            "clueless",
31            "lazy"
32          ]
33        }
34      }
35    },
36    {
37      "title":"Dog",
38      "type":"object",
39      "additionalProperties":false,
40      "properties":{
41        "animalType":{
42          "const":"Dog"
43        },
44        "breed":{
45          "title":"Dog Breed",
46          "type":"string",
47          "enum":[
48            "bulldog",
49            "bichons frise"
50          ]
51        }
52      }
53    }
54  ]
55}

Here animal is merged into cat and dog.

oneOf with properties

If both oneOf and properties are both present, it's interpreted exactly like oneOf with allOf. That means that the following:

1{
2  "title":"Animal",
3  "type":"object",
4  "properties":{
5    "animalType":{
6      "title":"Animal Type",
7      "type":"string"
8    },
9    "age":{
10      "type":"integer",
11      "min":0
12    }
13  },
14  "oneOf":[
15    {
16      "title":"Cat",
17      "type":"object",
18      "properties":{
19        "animalType":{
20          "const":"Cat"
21        },
22        "huntingSkill":{
23          "title":"Hunting Skill",
24          "type":"string",
25          "enum":[
26            "clueless",
27            "lazy"
28          ]
29        }
30      }
31    },
32    {
33      "title":"Dog",
34      "type":"object",
35      "additionalProperties":false,
36      "properties":{
37        "animalType":{
38          "const":"Dog"
39        },
40        "breed":{
41          "title":"Dog Breed",
42          "type":"string",
43          "enum":[
44            "bulldog",
45            "bichons frise"
46          ]
47        }
48      }
49    }
50  ]
51}

where all the defined behavior on the root object are merged into the two oneOf models cat and dog.

Interpreter

The main functionality is located in the Interpreter class. This class ensures to recursively create (or retrieve from a cache) a CommonModel representation of a Schema. We have tried to keep the functionality split out into separate functions to reduce complexity and ensure it is easy to maintain.

The order of interpretation:

  • true boolean schema infers all model types (object, string, number, array, boolean, null, integer) schemas.
  • type infers the initial model type.
  • required are interpreted as is.
  • patternProperties are merged together with any additionalProperties, where duplicate pattern models are merged.
  • additionalProperties are interpreted as is, where duplicate additionalProperties for the model are merged. If the schema does not define additionalProperties it defaults to true schema.
  • additionalItems are interpreted as is, where duplicate additionalItems for the model are merged. If the schema does not define additionalItems it defaults to true schema.
  • items are interpreted as ether tuples or simple array, where more than 1 item are merged. Usage of items infers array model type.
  • properties are interpreted as is, where duplicate properties for the model are merged. Usage of properties infers object model type.
  • allOf
  • dependencies only apply to schema dependencies, since property dependencies adds nothing to the underlying model. Any schema dependencies are interpreted and then merged together with the current interpreted model.
  • enum is interpreted as is, where each enum. Usage of enum infers the enumerator value type to the model, but only if the schema does not have type specified.
  • const interpretation overwrite already interpreted enum. Usage of const infers the constant value type to the model, but only if the schema does not have type specified.
  • allOf/oneOf/anyOf/then/else
  • not

Interpreting not schemas

not schemas infer the form for which the model should not take by recursively interpret the not schema. It removes certain model properties when encountered.

Currently, the following not model properties are interpreted:

  • type
  • enum

Restrictions

  • You cannot use nested not schemas to infer new model properties, it can only be used to re-allow them.
  • boolean not schemas are not applied.

Processing sub schemas

The following JSON Schema keywords are merged with the already interpreted model:

  • allOf
  • oneOf
  • anyOf
  • then
  • else

Merging models

Because of the recursive nature of the interpreter (and the nested nature of JSON Schema) it happens that two models needs to be merged together.

If only one side has a property defined, it is used as is, if both have it defined they are merged based on the following logic (look here for more information about the CommonModel and its properties):

  • additionalProperties if both models contain it the two are recursively merged together.
  • patternProperties if both models contain it each pattern model are recursively merged together.
  • properties if both models contain the same property the corresponding models are recursively merged together.
  • items are merged together based on a couple of rules:
    • If both models are simple arrays those item models are merged together as is.
    • If both models are tuple arrays each tuple model (at specific index) is merged together.
    • If either one side is different from the other, the tuple schemas is prioritized as it is more restrictive.
  • types if both models contain types they are merged together, duplicate types are removed.
  • enum if both models contain enums they are merged together, duplicate enums are removed.
  • required if both models contain required properties they are merged together, duplicate required properties are removed.
  • $id, $ref, extend uses left model value if present otherwise right model value if present.
  • originalSchema are overwritten.