JSON Schema is a highly readable, JSON-based schema language which offers similar capabilities for JSON as the XML Schema does for XML. In NRP-core it is used to define the structure of the experiment configuration as well as constraints and default values of the different parameters. At run-time, it is used to validate the simulation configuration file and to set default values for those parameters which are not present in this configuration file.
JSON Schema allows for composability and inheritance. Taking advantage of this capability, the simulation configuration schema is distributed in different sub-schemas, which are then composed or inherited as needed. Composition can be achieved using the ref keyword, which allows to reference other schemas. Inheritance is achieved by using allOf, which allows to validate against all the schemas referenced or defined inside of the allOf section. In this way an inherited schema can include a reference to its base schema and its own definition overriding, extending or over-constraining the defined parameters. An example of inheritance can be found in each of the engine schemas.
All JSON schemas in the framework are contained in the folder config_schemas
at the root of the project. Given the readability of json-schema, these schemas can be used directly as a source of documentation to look up each of the parameters in the configuration.
As an example, the schema corresponding to simulation, the top level schema which is used to validate a simulation configuration file is given below.
Each JSON schema can have an id which enables one to reference them in other schemas. The properties
dictionary contains the definition of all the parameters in the schema. Each of these parameters has a type
, a description
(used only for documentation purposes) and optionally a default value.
The type
of a parameter can be a simple type or a reference to another schema. A reference to another schema is formatted as a standard URI, with the next parts:
config_schemas
folderFor example "json://nrp-core/engines/engine_base.json#EngineBase"
references a schema with id EngineBase
which is stored in config_schemas/engines/engine_base.json
file and was implemented by the NRP Team. It must be noticed that even though the reference looks like a URL it is actually a URI, meaning that it might not (and usually won't) point to a resource that can be found online.
There is one limitation when referencing a schema by its id
. json-schema allows to define multiple schemas in a single file. In this case, the top-level JSON object can be a dictionary storing all the defined schemas. For an example see config_schemas/engines/engines_nest.json
. In this case in which the schema is not define at the top-level of the JSON object referencing it by id
doesn't work. The alternative is to reference it by the path to the schema in the JSON object. For example, if the schema below is stored in a file named example.json
:
Engine1
could be referenced as:
For a more in-depth explanation of the json-schema format details, https://json-schema.org is full of guides and information.
Default values of schema parameters are used to automatically set a value for the corresponding parameter if it has not been explicitly set in the configuration file. In this way, if a parameter is not present in the configuration file but it has a default value in the schema, it will be automatically added. This step is performed after validating the configuration file.
Those parameters which can't be left unset in the configuration file must be marked as required in the schema. For that they must be included in the required
list in the schema JSON object, as it is done in the simulation schema listed above. Usually, required parameters don't have a default value and vice versa, i.e. if a parameter is not required, a default value is defined for it in the schema. There are two exceptions to this rule:
[]
) are disregarded.To undertake these exceptions, default values can also be set in-code using the convenience method json_utils::setDefault. If a default value for a certain parameter is to be set in-code using this method, it should not be set as required in the corresponding schema and a default value should not be defined for it in this schema.
Finally, when using allOf
to define schema hierarchies (e.g. in engine schemas), all the schemas specified within the allOf
section will be traversed in order. An important implication of this behavior is that the first default value found for a parameter will be used. Therefore, in case of wanting to override default values in an inherited schema, the reference to the base schema should be place at the end of the allOf
list.