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:
trueboolean schema infers all model types (object,string,number,array,boolean,null,integer) schemas.typeinfers the initial model type.requiredare interpreted as is.patternPropertiesare merged together with any additionalProperties, where duplicate pattern models are merged.additionalPropertiesare interpreted as is, where duplicate additionalProperties for the model are merged. If the schema does not defineadditionalPropertiesit defaults totrueschema.additionalItemsare interpreted as is, where duplicate additionalItems for the model are merged. If the schema does not defineadditionalItemsit defaults totrueschema.itemsare interpreted as ether tuples or simple array, where more than 1 item are merged. Usage ofitemsinfersarraymodel type.propertiesare interpreted as is, where duplicatepropertiesfor the model are merged. Usage ofpropertiesinfersobjectmodel type.- allOf
dependenciesonly 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.enumis interpreted as is, where eachenum. Usage ofenuminfers the enumerator value type to the model, but only if the schema does not havetypespecified.constinterpretation overwrite already interpretedenum. Usage ofconstinfers the constant value type to the model, but only if the schema does not havetypespecified.- 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:
typeenum
Restrictions
- You cannot use nested
notschemas to infer new model properties, it can only be used to re-allow them. - boolean
notschemas are not applied.
Processing sub schemas
The following JSON Schema keywords are merged with the already interpreted model:
allOfoneOfanyOfthenelse
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):
additionalPropertiesif both models contain it the two are recursively merged together.patternPropertiesif both models contain it each pattern model are recursively merged together.propertiesif both models contain the same property the corresponding models are recursively merged together.itemsare 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.
typesif both models contain types they are merged together, duplicate types are removed.enumif both models contain enums they are merged together, duplicate enums are removed.requiredif both models contain required properties they are merged together, duplicate required properties are removed.$id,$ref,extenduses left model value if present otherwise right model value if present.originalSchemaare overwritten.