Validation¶
The validation of the JSON input files provided by the user is made possible by the getValue< ExpectedType >
function and special keys and KeyPath
s, as explained in Enhanced value access. This validator is customisable, i.e. the user can define a set of keys in their input file that will determine the behaviour of the validator under certain circumstances.
Validator options¶
The following keys of the mainJson
object can be defined to customise the behaviour of the validator:
- options.defaultValueUsedForMissingKey: determines the behaviour of the validator when the
getValue( const nlohmann::json& j, const KeyPath& keyPath, const ValueType& defaultValue )
function is called andkeyPath
is undefined. By default,defaultValue
is used without informing the user.- options.unusedKey: determines the behaviour of the validator when the
void checkUnusedKeys( const nlohmann::json& mainJson, ... )
function is called and some of the keys defined inmainJson
have not been accessed. This function is called by thejson_interface
application right before integrating the equations of motion, when all the settings objects have been created. By default, a warning is printed for each unused key.
These three options are converted to a value of the enum ExceptionResponseType
defined in Tudat/JsonInterface/Support/errorHandling.h
. The three possible values are:
continueSilently
: allow this validator feature and do not inform the user.printWarning
: allow this validator feature but print a warning when using it.throwError
: do not allow this validator feature and terminate throwing an error when trying to use this feature. For defaultValueUsedForMissingKey, anUndefinedKeyError
will be thrown (i.e. the behaviour of thegetValue
function with and without third default value argument will be equivalent). For unusedKey, the error messageValidation failed because there are unused keys.
will be printed after providing the list of unused keys.
Access history¶
The validator feature to check the keys that have not been used is useful to inform the user about the existence of redundant information in their input file, the use of old key identifiers that are not used anymore in the current version of Tudat, or the detection of typos in optional keys when manually writing the input file. Consider the following:
nlohmann::json j = { { "tyep", "euler" }, { "stepSize": 20 } }; // not typo in key "tyep"
std::string type = getValue( j, "type", "rungeKutta4" );
This would result in the integrator being used to be of the default type rungeKutta4
even though the intention of the user was to use of type euler
. However, the user will not know about this unless they have configured the validator to warn about the use of default values (which is off by default). Thus, before integrating the equations of motion, when all the required information has been retrieved from the mainJson
object, the call to checkUnusedKeys
will print the following warning (by default):
Unused key: integrator.tyep
allowing the user to quickly identify and fix the problem. The validator can also be configured not to print a warning (this is highly discouraged) or to terminate in case that there are unused keys.
In order to make this feature possible, the json_interface
has to keep track of all the key paths of mainJson
that have been accessed, and then iterate over all its keys to check whether any of them has not been used. Keeping track of the accessed key paths is done by calling the getValue
function (i.e. using the default value access methods will not keep track of the used keys). The original implementation proposal consisted in defining a special key, e.g. #accessedKeyPaths
, for the mainJson
object, in which an array of KeyPath
s would be stored. However, the mainJson
object is not always accessible in the from_json
functions. For instance, in the following code it is accessible:
#include "integrator.h"
void from_json( const nlohmann::json& mainJson, Simulation& simulation )
{
simulation.integrator =
getValue< Integrator >( mainJson, "integrator" ); // integrators::from_json called
}
so the getValue
function could potentially modify mainJson
to add integrator
as an accessed key, but here the mainJson
object is not accessible:
namespace integrators
{
void from_json( const nlohmann::json& jsonIntegrator, Integrator& integrator )
{
integrator.type = getValue< std::string >( jsonIntegrator, "type" );
integrator.stepSize = getValue< double >( jsonIntegrator, "stepSize" );
}
}
and thus is is not possible for the getValue
to add the integrator.type
and integrator.stepSize
key paths to the mainObject
’s #accessedKeyPaths
special key.
A possible walk-around this issue could consist in defining the #accessedKeyPaths
for each nlohmann::json
object, and not only for the mainJson
. However, the updated sub-nlohmann::json
objects would still have to be passed back to the mainJson
somehow. Even if this was possible, the definition of the from_json
function, with the first argument as a const nlohmann::json&
makes this approach unfeasible, as the getValue
function would not be capable of updating it (it will not be possible to update its #accessedKeyPaths
because it is a constant object). Thus, a completely different approach had to be followed, making use of a global variable. This global variable is declared in Tudat/JsonInterface/Support/valueAccess.h
:
extern std::set< KeyPath > accessedKeyPaths;
This variable is automatically updated when calling the getValue
function. In order to clear the contents of this variable, the function clearAccessHistory
must be called. In the json_interface
application, this is done after reading the input JSON file and just before starting to use the read nlohmann::json
object to update the settings objects.
Warning
The current implementation has one limitation: it is not possible to keep track of the accessed keys of multiple nlohmann::json
objects simultaneously. Currently, this is not done anywhere in the JSON Interface library, but if in the feature this is required, it will be necessary to create a derived class of nlohmann::json
(e.g. EnhancedJSON
) with the accessedKeyPaths
as property and the functions getValue
and checkUnusedKeys
as methods (probably other global functions declared in Tudat/JsonInterface/Support/valueAccess.h
would also have to be moved to this class). The to_json
and from_json
methods would have to be updated to take objects of this class as first argument instead of the basic nlohmann::json
objects. An attempt to implement this was done at one point during the development of the json_interface
, but it was unsuccessful due to the existence of an inheritance bug in the JSON library.