mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2025-03-01 10:33:28 +00:00
JSON Auto correction + hyperion schema split for better readability (#452)
* revoke schema split * add "getAutoCorrectedConfig" function * revoke schema split * revoke schema split * revoke schema split * Prevent compiler error if none grabber is available * revoke schema split * add "getAutoCorrectedConfig" function * revoke schema split * remove "configMigrator" * remove "configMigrator" * Change TestConfigFile to show how the autocorrection works * revoke schema split * revoke schema split * remove "ConfigMigrator" * remove "ConfigMigrator" * remove "ConfigMigratorBase" * remove "ConfigMigratorBase" * Add QJsonUtils.h * added ability "ignore-required" It has been added the ability to correct the configuration without having to pay attention to the keyword "required" in the hyperion schema * Allow Comments in Hyperion Schema * add ability to ignore the "required" keyword in hyperion schema * add ability to ignore the "required" keyword in hyperion schema * add ability to ignore the "required" keyword in hyperion schema * //Allow Comments in Hyperion Schema * Update jsonschema.py to version 0.8.0 to support ... references in json schema * add RefResolver from jsonschema.py to resolve references in hyperion schema * remove dupe code * split the hyperion schema in separatly files For better readability * add function "resolveReferences" to resolve references in hyperion schema. * remove CURRENT_CONFIG_VERSION * remove CURRENT_CONFIG_VERSION * split the hyperion schema in separatly files For better readability * Create schema-backgroundEffect.json * Add the rest of the Hyperion schema via upload * Remove Comments in config file * Add return variable to function writeJson(). fix function resolveReferences(). edit function load() to handle QPair result from schemaChecker. * edit function validate() to return QPair variable * fit function loadEffectDefinition() * fit function checkJson() * Expand error check by dividing "_error" variable in "_error" and "_schemaError". Replace variable "bool" in validate() in QPair * Extend function "cmd_cfg_set" to handle auto correction * Extend function "loadConfig" to handle auto correction * fix function loadConfig()
This commit is contained in:
committed by
brindosch
parent
622a171808
commit
5bd020a570
@@ -25,17 +25,14 @@ public:
|
||||
// create the validator
|
||||
QJsonSchemaChecker schemaChecker;
|
||||
schemaChecker.setSchema(schemaTree);
|
||||
|
||||
bool valid = schemaChecker.validate(configTree);
|
||||
|
||||
QStringList messages = schemaChecker.getMessages();
|
||||
for (int i = 0; i < messages.size(); ++i)
|
||||
{
|
||||
std::cout << messages[i].toStdString() << std::endl;
|
||||
}
|
||||
|
||||
if (!valid)
|
||||
|
||||
if (!schemaChecker.validate(configTree).first)
|
||||
{
|
||||
for (int i = 0; i < messages.size(); ++i)
|
||||
std::cout << messages[i].toStdString() << std::endl;
|
||||
|
||||
std::cerr << "Validation failed for configuration file: " << config.toStdString() << std::endl;
|
||||
return -3;
|
||||
}
|
||||
@@ -54,6 +51,7 @@ public:
|
||||
throw std::runtime_error(QString("Configuration file not found: '" + path + "' (" + file.errorString() + ")").toStdString());
|
||||
}
|
||||
|
||||
//Allow Comments in Config
|
||||
QString config = QString(file.readAll());
|
||||
config.remove(QRegularExpression("([^:]?\\/\\/.*)"));
|
||||
|
||||
@@ -112,23 +110,62 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
throw std::runtime_error(QString("ERROR: Json schema wrong: " + error.errorString() + " at Line: " + QString::number(errorLine)
|
||||
+ ", Column: " + QString::number(errorColumn)).toStdString()
|
||||
);
|
||||
throw std::runtime_error(QString("ERROR: Json schema wrong: " + error.errorString() +
|
||||
" at Line: " + QString::number(errorLine) +
|
||||
", Column: " + QString::number(errorColumn)).toStdString());
|
||||
}
|
||||
|
||||
return doc.object();
|
||||
return resolveReferences(doc.object());
|
||||
}
|
||||
|
||||
static void writeJson(const QString& filename, QJsonObject& jsonTree)
|
||||
static QJsonObject resolveReferences(const QJsonObject& schema)
|
||||
{
|
||||
QJsonObject result;
|
||||
|
||||
for (QJsonObject::const_iterator i = schema.begin(); i != schema.end(); ++i)
|
||||
{
|
||||
QString attribute = i.key();
|
||||
const QJsonValue & attributeValue = *i;
|
||||
|
||||
if (attribute == "$ref" && attributeValue.isString())
|
||||
{
|
||||
try
|
||||
{
|
||||
result = readSchema(":/" + attributeValue.toString());
|
||||
}
|
||||
catch (std::runtime_error& error)
|
||||
{
|
||||
throw std::runtime_error(error.what());
|
||||
}
|
||||
}
|
||||
else if (attributeValue.isObject())
|
||||
result.insert(attribute, resolveReferences(attributeValue.toObject()));
|
||||
else
|
||||
result.insert(attribute, attributeValue);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool writeJson(const QString& filename, QJsonObject& jsonTree)
|
||||
{
|
||||
QJsonDocument doc;
|
||||
|
||||
doc.setObject(jsonTree);
|
||||
QByteArray configData = doc.toJson(QJsonDocument::Indented);
|
||||
|
||||
|
||||
QFile configFile(filename);
|
||||
configFile.open(QIODevice::WriteOnly | QIODevice::Truncate);
|
||||
if (!configFile.open(QFile::WriteOnly | QFile::Truncate))
|
||||
return false;
|
||||
|
||||
configFile.write(configData);
|
||||
|
||||
QFile::FileError error = configFile.error();
|
||||
if (error != QFile::NoError)
|
||||
return false;
|
||||
|
||||
configFile.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
@@ -5,6 +5,7 @@
|
||||
#include <QJsonValue>
|
||||
#include <QJsonArray>
|
||||
#include <QStringList>
|
||||
#include <QPair>
|
||||
|
||||
/// JsonSchemaChecker is a very basic implementation of json schema.
|
||||
/// The json schema definition draft can be found at
|
||||
@@ -33,13 +34,23 @@ public:
|
||||
/// @return true upon succes
|
||||
///
|
||||
bool setSchema(const QJsonObject & schema);
|
||||
|
||||
|
||||
///
|
||||
/// @brief Validate a JSON structure
|
||||
/// @param value The JSON value to check
|
||||
/// @return true when the arguments is valid according to the schema
|
||||
/// @param ignoreRequired Ignore the "required" keyword in hyperion schema. Default is false
|
||||
/// @return The first boolean is true when the arguments is valid according to the schema. The second is true when the schema contains no errors
|
||||
/// @return TODO: Check the Schema in SetSchema() function and remove the QPair result
|
||||
///
|
||||
bool validate(const QJsonObject & value, bool ignoreRequired = false);
|
||||
QPair<bool, bool> validate(const QJsonObject & value, bool ignoreRequired = false);
|
||||
|
||||
///
|
||||
/// @brief Auto correct a JSON structure
|
||||
/// @param value The JSON value to correct
|
||||
/// @param ignoreRequired Ignore the "required" keyword in hyperion schema. Default is false
|
||||
/// @return The corrected JSON structure
|
||||
///
|
||||
QJsonObject getAutoCorrectedConfig(const QJsonObject & value, bool ignoreRequired = false);
|
||||
|
||||
///
|
||||
/// @return A list of error messages
|
||||
@@ -72,7 +83,7 @@ private:
|
||||
/// @param[in] value The given value
|
||||
/// @param[in] schema The specified type (as json-value)
|
||||
///
|
||||
void checkType(const QJsonValue & value, const QJsonValue & schema);
|
||||
void checkType(const QJsonValue & value, const QJsonValue & schema, const QJsonValue & defaultValue);
|
||||
|
||||
///
|
||||
/// Checks is required properties of an json-object exist and if all properties are of the
|
||||
@@ -101,7 +112,7 @@ private:
|
||||
/// @param[in] value The given value
|
||||
/// @param[in] schema The minimum value (as json-value)
|
||||
///
|
||||
void checkMinimum(const QJsonValue & value, const QJsonValue & schema);
|
||||
void checkMinimum(const QJsonValue & value, const QJsonValue & schema, const QJsonValue & defaultValue);
|
||||
|
||||
///
|
||||
/// Checks if the given value is smaller or equal to the specified value. If this is not the
|
||||
@@ -110,7 +121,7 @@ private:
|
||||
/// @param[in] value The given value
|
||||
/// @param[in] schema The maximum value (as json-value)
|
||||
///
|
||||
void checkMaximum(const QJsonValue & value, const QJsonValue & schema);
|
||||
void checkMaximum(const QJsonValue & value, const QJsonValue & schema, const QJsonValue & defaultValue);
|
||||
|
||||
///
|
||||
/// Checks if the given value is hugher than the specified value. If this is the
|
||||
@@ -119,7 +130,7 @@ private:
|
||||
/// @param value The given value
|
||||
/// @param schema The minimum size specification (as json-value)
|
||||
///
|
||||
void checkMinLength(const QJsonValue & value, const QJsonValue & schema);
|
||||
void checkMinLength(const QJsonValue & value, const QJsonValue & schema, const QJsonValue & defaultValue);
|
||||
|
||||
///
|
||||
/// Checks if the given value is smaller than the specified value. If this is the
|
||||
@@ -128,7 +139,7 @@ private:
|
||||
/// @param value The given value
|
||||
/// @param schema The maximum size specification (as json-value)
|
||||
///
|
||||
void checkMaxLength(const QJsonValue & value, const QJsonValue & schema);
|
||||
void checkMaxLength(const QJsonValue & value, const QJsonValue & schema, const QJsonValue & defaultValue);
|
||||
|
||||
///
|
||||
/// Validates all the items of an array.
|
||||
@@ -145,7 +156,7 @@ private:
|
||||
/// @param value The json-array
|
||||
/// @param schema The minimum size specification (as json-value)
|
||||
///
|
||||
void checkMinItems(const QJsonValue & value, const QJsonValue & schema);
|
||||
void checkMinItems(const QJsonValue & value, const QJsonValue & schema, const QJsonValue & defaultValue);
|
||||
|
||||
///
|
||||
/// Checks if a given array has at most a maximum number of items. If this is not the case
|
||||
@@ -154,7 +165,7 @@ private:
|
||||
/// @param value The json-array
|
||||
/// @param schema The maximum size specification (as json-value)
|
||||
///
|
||||
void checkMaxItems(const QJsonValue & value, const QJsonValue & schema);
|
||||
void checkMaxItems(const QJsonValue & value, const QJsonValue & schema, const QJsonValue & defaultValue);
|
||||
|
||||
///
|
||||
/// Checks if a given array contains only unique items. If this is not the case
|
||||
@@ -172,17 +183,23 @@ private:
|
||||
/// @param value The enum value
|
||||
/// @param schema The enum schema definition
|
||||
///
|
||||
void checkEnum(const QJsonValue & value, const QJsonValue & schema);
|
||||
void checkEnum(const QJsonValue & value, const QJsonValue & schema, const QJsonValue & defaultValue);
|
||||
|
||||
private:
|
||||
/// The schema of the entire json-configuration
|
||||
QJsonObject _qSchema;
|
||||
/// ignore the required value in json schema
|
||||
bool _ignoreRequired;
|
||||
/// Auto correction variable
|
||||
QString _correct;
|
||||
/// The auto corrected json-configuration
|
||||
QJsonObject _autoCorrected;
|
||||
/// The current location into a json-configuration structure being checked
|
||||
QStringList _currentPath;
|
||||
/// The result messages collected during the schema verification
|
||||
QStringList _messages;
|
||||
/// Flag indicating an error occured during validation
|
||||
bool _error;
|
||||
/// Flag indicating an schema error occured during validation
|
||||
bool _schemaError;
|
||||
};
|
||||
|
273
include/utils/jsonschema/QJsonUtils.h
Normal file
273
include/utils/jsonschema/QJsonUtils.h
Normal file
@@ -0,0 +1,273 @@
|
||||
#pragma once
|
||||
|
||||
// QT include
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonValue>
|
||||
#include <QJsonArray>
|
||||
|
||||
class QJsonUtils
|
||||
{
|
||||
public:
|
||||
|
||||
static void modify(QJsonObject& value, QStringList path, const QJsonValue& newValue = QJsonValue::Null, QString propertyName = "")
|
||||
{
|
||||
QJsonObject result;
|
||||
|
||||
if (!path.isEmpty())
|
||||
{
|
||||
if (path.first() == "[root]")
|
||||
path.removeFirst();
|
||||
|
||||
for (QStringList::iterator it = path.begin(); it != path.end(); ++it)
|
||||
{
|
||||
QString current = *it;
|
||||
if (current.left(1) == ".")
|
||||
*it = current.mid(1, current.size()-1);
|
||||
}
|
||||
|
||||
if (!value.isEmpty())
|
||||
modifyValue(value, result, path, newValue, propertyName);
|
||||
else if (newValue != QJsonValue::Null && !propertyName.isEmpty())
|
||||
result[propertyName] = newValue;
|
||||
}
|
||||
|
||||
value = result;
|
||||
}
|
||||
|
||||
static QJsonValue create(QJsonValue schema, bool ignoreRequired = false)
|
||||
{
|
||||
return createValue(schema, ignoreRequired);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
static QJsonValue createValue(QJsonValue schema, bool ignoreRequired)
|
||||
{
|
||||
QJsonObject result;
|
||||
QJsonObject obj = schema.toObject();
|
||||
|
||||
if (obj.find("type") != obj.end() && obj.find("type").value().isString())
|
||||
{
|
||||
QJsonValue ret = QJsonValue::Null;
|
||||
|
||||
if (obj.find("type").value().toString() == "object" && ( obj.find("required").value().toBool() || ignoreRequired ) )
|
||||
ret = createValue(obj["properties"], ignoreRequired);
|
||||
else if (obj.find("type").value().toString() == "array" && ( obj.find("required").value().toBool() || ignoreRequired ) )
|
||||
{
|
||||
QJsonArray array;
|
||||
|
||||
if (obj.find("default") != obj.end())
|
||||
ret = obj.find("default").value();
|
||||
else
|
||||
{
|
||||
ret = createValue(obj["items"], ignoreRequired);
|
||||
|
||||
if (!ret.toObject().isEmpty())
|
||||
array.append(ret);
|
||||
ret = array;
|
||||
}
|
||||
}
|
||||
else if ( obj.find("required").value().toBool() || ignoreRequired )
|
||||
if (obj.find("default") != obj.end())
|
||||
ret = obj.find("default").value();
|
||||
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (QJsonObject::const_iterator i = obj.begin(); i != obj.end(); ++i)
|
||||
{
|
||||
QString attribute = i.key();
|
||||
const QJsonValue & attributeValue = *i;
|
||||
QJsonValue subValue = obj[attribute];
|
||||
|
||||
if (attributeValue.toObject().find("type") != attributeValue.toObject().end())
|
||||
{
|
||||
if (attributeValue.toObject().find("type").value().toString() == "object" && ( attributeValue.toObject().find("required").value().toBool() || ignoreRequired ) )
|
||||
{
|
||||
if (obj.contains("properties"))
|
||||
result[attribute] = createValue(obj["properties"], ignoreRequired);
|
||||
else
|
||||
result[attribute] = createValue(subValue, ignoreRequired);
|
||||
}
|
||||
else if (attributeValue.toObject().find("type").value().toString() == "array" && ( attributeValue.toObject().find("required").value().toBool() || ignoreRequired ) )
|
||||
{
|
||||
QJsonArray array;
|
||||
|
||||
if (attributeValue.toObject().find("default") != attributeValue.toObject().end())
|
||||
result[attribute] = attributeValue.toObject().find("default").value();
|
||||
else
|
||||
{
|
||||
QJsonValue retEmpty;
|
||||
retEmpty = createValue(attributeValue.toObject()["items"], ignoreRequired);
|
||||
|
||||
if (!retEmpty.toObject().isEmpty())
|
||||
array.append(retEmpty);
|
||||
result[attribute] = array;
|
||||
}
|
||||
}
|
||||
else if ( attributeValue.toObject().find("required").value().toBool() || ignoreRequired )
|
||||
{
|
||||
if (attributeValue.toObject().find("default") != attributeValue.toObject().end())
|
||||
result[attribute] = attributeValue.toObject().find("default").value();
|
||||
else
|
||||
result[attribute] = QJsonValue::Null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void modifyValue(QJsonValue source, QJsonObject& target, QStringList path, const QJsonValue& newValue, QString& property)
|
||||
{
|
||||
QJsonObject obj = source.toObject();
|
||||
|
||||
if (!obj.isEmpty())
|
||||
{
|
||||
for (QJsonObject::iterator i = obj.begin(); i != obj.end(); ++i)
|
||||
{
|
||||
QString propertyName = i.key();
|
||||
QJsonValue subValue = obj[propertyName];
|
||||
|
||||
if (subValue.isObject())
|
||||
{
|
||||
if (!path.isEmpty())
|
||||
{
|
||||
if (propertyName == path.first())
|
||||
{
|
||||
path.removeFirst();
|
||||
|
||||
if (!path.isEmpty())
|
||||
{
|
||||
QJsonObject obj;
|
||||
modifyValue(subValue, obj, path, newValue, property);
|
||||
subValue = obj;
|
||||
}
|
||||
else if (newValue != QJsonValue::Null)
|
||||
subValue = newValue;
|
||||
else
|
||||
continue;
|
||||
|
||||
if (!subValue.toObject().isEmpty())
|
||||
target[propertyName] = subValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (path.first() == property && newValue != QJsonValue::Null)
|
||||
{
|
||||
target[property] = newValue;
|
||||
property = QString();
|
||||
}
|
||||
|
||||
target[propertyName] = subValue;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (!subValue.toObject().isEmpty())
|
||||
target[propertyName] = subValue;
|
||||
}
|
||||
else if (subValue.isArray())
|
||||
{
|
||||
if (!path.isEmpty())
|
||||
{
|
||||
if (propertyName == path.first())
|
||||
{
|
||||
path.removeFirst();
|
||||
|
||||
int arrayLevel = -1;
|
||||
if (!path.isEmpty())
|
||||
{
|
||||
if ((path.first().left(1) == "[") && (path.first().right(1) == "]"))
|
||||
{
|
||||
arrayLevel = path.first().mid(1, path.first().size()-2).toInt();
|
||||
path.removeFirst();
|
||||
}
|
||||
}
|
||||
|
||||
QJsonArray array = subValue.toArray();
|
||||
QJsonArray json_array;
|
||||
|
||||
for (QJsonArray::iterator i = array.begin(); i != array.end(); ++i)
|
||||
{
|
||||
if (!path.isEmpty())
|
||||
{
|
||||
QJsonObject arr;
|
||||
modifyValue(*i, arr, path, newValue, property);
|
||||
subValue = arr;
|
||||
}
|
||||
else if (newValue != QJsonValue::Null)
|
||||
subValue = newValue;
|
||||
else
|
||||
continue;
|
||||
|
||||
if (!subValue.toObject().isEmpty())
|
||||
json_array.append(subValue);
|
||||
else if (newValue != QJsonValue::Null && arrayLevel != -1)
|
||||
json_array.append( (i - array.begin() == arrayLevel) ? subValue : *i );
|
||||
}
|
||||
|
||||
if (!json_array.isEmpty())
|
||||
target[propertyName] = json_array;
|
||||
else if (newValue != QJsonValue::Null && arrayLevel == -1)
|
||||
target[propertyName] = newValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (path.first() == property && newValue != QJsonValue::Null)
|
||||
{
|
||||
target[property] = newValue;
|
||||
property = QString();
|
||||
}
|
||||
|
||||
target[propertyName] = subValue;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (!subValue.toArray().isEmpty())
|
||||
target[propertyName] = subValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!path.isEmpty())
|
||||
{
|
||||
if (propertyName == path.first())
|
||||
{
|
||||
path.removeFirst();
|
||||
|
||||
if (path.isEmpty())
|
||||
{
|
||||
if (newValue != QJsonValue::Null && property.isEmpty())
|
||||
subValue = newValue;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
|
||||
target[propertyName] = subValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (path.first() == property && newValue != QJsonValue::Null)
|
||||
{
|
||||
target[property] = newValue;
|
||||
property = QString();
|
||||
}
|
||||
|
||||
target[propertyName] = subValue;
|
||||
}
|
||||
}
|
||||
else
|
||||
target[propertyName] = subValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (newValue != QJsonValue::Null && !property.isEmpty())
|
||||
{
|
||||
target[property] = newValue;
|
||||
property = QString();
|
||||
}
|
||||
}
|
||||
};
|
Reference in New Issue
Block a user