Add json results format validation

This commit is contained in:
2025-01-07 11:58:18 +01:00
parent 0318dcf8e5
commit 4901bb1f32
6 changed files with 305 additions and 2 deletions

129
src/results/JsonValidator.h Normal file
View File

@@ -0,0 +1,129 @@
#ifndef JSONVALIDATOR_H
#define JSONVALIDATOR_H
#include <fstream>
#include <vector>
#include <regex>
#include <nlohmann/json.hpp>
namespace platform {
using json = nlohmann::ordered_json;
class JsonValidator {
public:
JsonValidator(const json& schema) : schema(schema) {}
std::vector<std::string> validate(const std::string& fileName)
{
std::ifstream file(fileName);
if (!file.is_open()) {
return { "Error: Unable to open file." };
}
json data;
try {
file >> data;
}
catch (const json::parse_error& e) {
return { "Error: JSON parsing failed: " + std::string(e.what()) };
}
std::vector<std::string> errors;
// Validate all fields defined in the schema
for (const auto& [key, value] : schema["properties"].items()) {
validateField(key, value, data, errors);
}
return errors;
}
void fix_it(const std::string& fileName)
{
std::ifstream file(fileName);
if (!file.is_open()) {
std::cerr << "Error: Unable to open file for fixing." << std::endl;
return;
}
json data;
try {
file >> data;
}
catch (const json::parse_error& e) {
std::cerr << "Error: JSON parsing failed: " << e.what() << std::endl;
return;
}
file.close();
// Fix fields
for (const auto& [key, value] : schema["properties"].items()) {
if (!data.contains(key)) {
// Set default value if specified in the schema
if (value.contains("default")) {
data[key] = value["default"];
} else if (value["type"] == "array") {
data[key] = json::array();
} else if (value["type"] == "object") {
data[key] = json::object();
} else {
data[key] = nullptr;
}
}
// Fix const fields to match the schema value
if (value.contains("const")) {
data[key] = value["const"];
}
}
// Save fixed JSON
std::ofstream outFile(fileName);
if (!outFile.is_open()) {
std::cerr << "Error: Unable to open file for writing." << std::endl;
return;
}
outFile << data.dump(4);
outFile.close();
}
private:
json schema;
void validateField(const std::string& field, const json& value, const json& data, std::vector<std::string>& errors)
{
// Check if the field is present
if (!data.contains(field)) {
errors.push_back("Missing required field: " + field);
return;
}
// Check for type constraints
if (value.contains("type")) {
const std::string type = value["type"];
if (type == "string" && !data[field].is_string()) {
errors.push_back("Field '" + field + "' should be a string.");
} else if (type == "number" && !data[field].is_number()) {
errors.push_back("Field '" + field + "' should be a number.");
} else if (type == "integer" && !data[field].is_number_integer()) {
errors.push_back("Field '" + field + "' should be an integer.");
} else if (type == "boolean" && !data[field].is_boolean()) {
errors.push_back("Field '" + field + "' should be a boolean.");
} else if (type == "array" && !data[field].is_array()) {
errors.push_back("Field '" + field + "' should be an array.");
} else if (type == "object" && !data[field].is_object()) {
errors.push_back("Field '" + field + "' should be an object.");
}
}
// Check for const constraints
if (value.contains("const")) {
const auto& expectedValue = value["const"];
if (data[field] != expectedValue) {
errors.push_back("Field '" + field + "' has an invalid value. Expected: " +
expectedValue.dump() + ", Found: " + data[field].dump());
}
}
}
};
}
#endif

103
src/results/SchemaV1_0.h Normal file
View File

@@ -0,0 +1,103 @@
#ifndef SCHEMAV1_0_H
#define SCHEMAV1_0_H
#include <nlohmann/json.hpp>
namespace platform {
using json = nlohmann::ordered_json;
class SchemaV1_0 {
public:
// Define JSON schema
const static json schema;
};
const json SchemaV1_0::schema = {
{"$schema", "http://json-schema.org/draft-07/schema#"},
{"type", "object"},
{"properties", {
{"schema_version", {
{"type", "string"},
{"pattern", "^\\d+\\.\\d+$"},
{"default", "1.0"},
{"const", "1.0"} // Fixed schema version for this schema
}},
{"date", {{"type", "string"}, {"format", "date"}}},
{"time", {{"type", "string"}, {"pattern", "^\\d{2}:\\d{2}:\\d{2}$"}}},
{"title", {{"type", "string"}}},
{"language", {{"type", "string"}}},
{"language_version", {{"type", "string"}}},
{"discretized", {{"type", "boolean"}, {"default", false}}},
{"model", {{"type", "string"}}},
{"platform", {{"type", "string"}}},
{"stratified", {{"type", "boolean"}, {"default", false}}},
{"folds", {{"type", "integer"}, {"default", 0}}},
{"score_name", {{"type", "string"}}},
{"version", {{"type", "string"}}},
{"duration", {{"type", "number"}, {"default", 0}}},
{"results", {
{"type", "array"},
{"items", {
{"type", "object"},
{"properties", {
{"scores_train", {{"type", "array"}, {"items", {{"type", "number"}}}}},
{"scores_test", {{"type", "array"}, {"items", {{"type", "number"}}}}},
{"times_train", {{"type", "array"}, {"items", {{"type", "number"}}}}},
{"times_test", {{"type", "array"}, {"items", {{"type", "number"}}}}},
{"notes", {{"type", "array"}, {"items", {{"type", "string"}}}}},
{"train_time", {{"type", "number"}, {"default", 0}}},
{"train_time_std", {{"type", "number"}, {"default", 0}}},
{"test_time", {{"type", "number"}, {"default", 0}}},
{"test_time_std", {{"type", "number"}, {"default", 0}}},
{"samples", {{"type", "integer"}, {"default", 0}}},
{"features", {{"type", "integer"}, {"default", 0}}},
{"classes", {{"type", "integer"}, {"default", 0}}},
{"hyperparameters", {
{"type", "object"},
{"additionalProperties", {
{"oneOf", {
{{"type", "number"}}, // Field can be a number
{{"type", "string"}} // Field can also be a string
}}
}}
}},
{"score", {{"type", "number"}, {"default", 0}}},
{"score_train", {{"type", "number"}, {"default", 0}}},
{"score_std", {{"type", "number"}, {"default", 0}}},
{"score_train_std", {{"type", "number"}, {"default", 0}}},
{"time", {{"type", "number"}, {"default", 0}}},
{"time_std", {{"type", "number"}, {"default", 0}}},
{"nodes", {{"type", "number"}, {"default", 0}}},
{"leaves", {{"type", "number"}, {"default", 0}}},
{"depth", {{"type", "number"}, {"default", 0}}},
{"dataset", {{"type", "string"}}},
{"confusion_matrices", {
{"type", "array"},
{"items", {
{"type", "object"},
{"patternProperties", {
{".*", {
{"type", "array"},
{"items", {{"type", "integer"}}}
}}
}},
{"additionalProperties", false}
}}
}}
}},
{"required", {
"scores_train", "scores_test", "times_train", "times_test",
"notes", "train_time", "train_time_std", "test_time", "test_time_std",
"samples", "features", "classes", "hyperparameters", "score", "score_train",
"score_std", "score_train_std", "time", "time_std", "nodes", "leaves",
"depth", "dataset", "confusion_matrices"
}}
}}
}}
}},
{"required", {
"schema_version", "date", "time", "title", "language", "language_version",
"discretized", "model", "platform", "stratified", "folds", "score_name",
"version", "duration", "results"
}}
};
}
#endif