validator: handle undefined schema-loader via a std::function given at construction-time
This commit is contained in:
parent
685b759712
commit
998f97ba4e
@ -31,19 +31,37 @@ using nlohmann::json;
|
||||
using nlohmann::json_uri;
|
||||
using nlohmann::json_schema_draft4::json_validator;
|
||||
|
||||
static void loader(const json_uri &uri, json &schema)
|
||||
{
|
||||
std::map<std::string, std::string> external_schemas =
|
||||
{
|
||||
{"http://localhost:1234/integer.json", JSON_SCHEMA_TEST_SUITE_PATH "/remotes/integer.json"},
|
||||
{"http://localhost:1234/subSchemas.json", JSON_SCHEMA_TEST_SUITE_PATH "/remotes/subSchemas.json"},
|
||||
{"http://localhost:1234/folder/folderInteger.json", JSON_SCHEMA_TEST_SUITE_PATH "/remotes/folder/folderInteger.json"},
|
||||
};
|
||||
|
||||
if (uri.to_string() == "http://json-schema.org/draft-04/schema#") {
|
||||
schema = nlohmann::json_schema_draft4::draft4_schema_builtin;
|
||||
return;
|
||||
}
|
||||
|
||||
std::string fn = external_schemas[uri.url()];
|
||||
|
||||
std::fstream s(fn.c_str());
|
||||
if (!s.good())
|
||||
throw std::invalid_argument("could not open " + uri.url() + " for schema loading\n");
|
||||
|
||||
try {
|
||||
schema << s;
|
||||
} catch (std::exception &e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
json validation; // a validation case following the JSON-test-suite-schema
|
||||
|
||||
std::map<std::string, std::string> external_schemas;
|
||||
|
||||
external_schemas["http://localhost:1234/integer.json"] =
|
||||
JSON_SCHEMA_TEST_SUITE_PATH "/remotes/integer.json";
|
||||
external_schemas["http://localhost:1234/subSchemas.json"] =
|
||||
JSON_SCHEMA_TEST_SUITE_PATH "/remotes/subSchemas.json";
|
||||
external_schemas["http://localhost:1234/folder/folderInteger.json"] =
|
||||
JSON_SCHEMA_TEST_SUITE_PATH "/remotes/folder/folderInteger.json";
|
||||
|
||||
try {
|
||||
std::cin >> validation;
|
||||
} catch (std::exception &e) {
|
||||
@ -62,44 +80,7 @@ int main(void)
|
||||
|
||||
const auto &schema = test_group["schema"];
|
||||
|
||||
json_validator validator;
|
||||
do {
|
||||
std::set<json_uri> undefined;
|
||||
try {
|
||||
undefined = validator.insert_schema(schema, json_uri("#"));
|
||||
|
||||
} catch (std::exception &e) {
|
||||
std::cout << " Test Case Exception (root-schema-inserting): " << e.what() << "\n";
|
||||
}
|
||||
|
||||
if (undefined.size() == 0)
|
||||
break;
|
||||
|
||||
for (auto ref : undefined) {
|
||||
std::cerr << "missing schema URL " << ref << " - trying to load it\n";
|
||||
|
||||
if (ref.to_string() == "http://json-schema.org/draft-04/schema#")
|
||||
validator.insert_schema(nlohmann::json_schema_draft4::draft4_schema_builtin, ref);
|
||||
else {
|
||||
std::string fn = external_schemas[ref.url()];
|
||||
|
||||
std::fstream s(fn.c_str());
|
||||
if (!s.good()) {
|
||||
std::cerr << "could not open " << ref.url() << "\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
json extra;
|
||||
extra << s;
|
||||
|
||||
try {
|
||||
validator.insert_schema(extra, ref.url());
|
||||
} catch (std::exception &e) {
|
||||
std::cout << " Test Case Exception (schema-loading/inserting): " << e.what() << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} while (1);
|
||||
json_validator validator(schema, loader);
|
||||
|
||||
for (auto &test_case : test_group["tests"]) {
|
||||
std::cout << " Testing Case " << test_case["description"] << "\n";
|
||||
@ -109,17 +90,14 @@ int main(void)
|
||||
try {
|
||||
validator.validate(test_case["data"]);
|
||||
} catch (const std::out_of_range &e) {
|
||||
|
||||
valid = false;
|
||||
std::cout << " Test Case Exception (out of range): " << e.what() << "\n";
|
||||
|
||||
} catch (const std::invalid_argument &e) {
|
||||
|
||||
valid = false;
|
||||
std::cout << " Test Case Exception (invalid argument): " << e.what() << "\n";
|
||||
|
||||
} catch (const std::logic_error &e) {
|
||||
|
||||
valid = !test_case["valid"]; /* force test-case failure */
|
||||
std::cout << " Not yet implemented: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
@ -46,12 +46,24 @@ static void usage(const char *name)
|
||||
assert(r.undefined_refs.size() == 0);
|
||||
#endif
|
||||
|
||||
static void loader(const json_uri &uri, json &schema)
|
||||
{
|
||||
std::fstream lf(uri.path());
|
||||
if (!lf.good())
|
||||
throw std::invalid_argument("could not open " + uri.url());
|
||||
|
||||
try {
|
||||
lf >> schema;
|
||||
} catch (std::exception &e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc != 2)
|
||||
usage(argv[0]);
|
||||
|
||||
json_validator validator;
|
||||
|
||||
std::fstream f(argv[1]);
|
||||
if (!f.good()) {
|
||||
@ -69,41 +81,10 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
// 2) insert this schema to the validator
|
||||
// this resolves remote-schemas, sub-schemas and references
|
||||
bool error = false;
|
||||
do {
|
||||
// inserting with json_uri("#") means this is the document's root-schema
|
||||
auto missing_schemas = validator.insert_schema(schema, json_uri("#"));
|
||||
// this resolves remote-schemas, sub-schemas and references via the given loader-function
|
||||
json_validator validator(schema, loader);
|
||||
|
||||
// schema was inserted and all references have been fulfilled
|
||||
if (missing_schemas.size() == 0)
|
||||
break;
|
||||
|
||||
// schema was not inserted because it references unknown schemas
|
||||
// 3) load missing schemas and insert them
|
||||
for (auto ref : missing_schemas) {
|
||||
std::cerr << "missing schema URL " << ref << " - trying to load it\n";
|
||||
|
||||
std::fstream lf(ref.path());
|
||||
if (!lf.good()) {
|
||||
std::cerr << "could not open " << ref.url() << "\n";
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
json extra;
|
||||
try {
|
||||
lf >> extra;
|
||||
} catch (std::exception &e) {
|
||||
std::cerr << e.what() << " at " << lf.tellp() << "\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
validator.insert_schema(extra, json_uri(ref.url()));
|
||||
std::cerr << "OK";
|
||||
}
|
||||
} while (!error);
|
||||
|
||||
// 4) do the actual validation of the document
|
||||
// 3) do the actual validation of the document
|
||||
json document;
|
||||
|
||||
try {
|
||||
|
||||
@ -28,8 +28,6 @@
|
||||
|
||||
#include <json.hpp>
|
||||
|
||||
#include <set>
|
||||
|
||||
// make yourself a home - welcome to nlohmann's namespace
|
||||
namespace nlohmann
|
||||
{
|
||||
@ -161,6 +159,7 @@ class json_validator
|
||||
{
|
||||
std::vector<std::shared_ptr<json>> schema_store_;
|
||||
std::shared_ptr<json> root_schema_;
|
||||
std::function<void(const json_uri &, json &)> schema_loader_ = nullptr;
|
||||
|
||||
std::map<json_uri, const json *> schema_refs_;
|
||||
|
||||
@ -168,8 +167,10 @@ class json_validator
|
||||
void validate_array(json &instance, const json &schema_, const std::string &name);
|
||||
void validate_object(json &instance, const json &schema_, const std::string &name);
|
||||
|
||||
void insert_schema(const json &input, const json_uri &id);
|
||||
|
||||
public:
|
||||
std::set<json_uri> insert_schema(const json &input, json_uri id);
|
||||
json_validator(const json &schema, std::function<void(const json_uri &, json &)> loader);
|
||||
|
||||
void validate(json &instance);
|
||||
|
||||
|
||||
@ -271,43 +271,51 @@ namespace nlohmann
|
||||
namespace json_schema_draft4
|
||||
{
|
||||
|
||||
std::set<json_uri> json_validator::insert_schema(const json &input, json_uri id)
|
||||
void json_validator::insert_schema(const json &input, const json_uri &id)
|
||||
{
|
||||
// allocate create a copy for later storage - if resolving reference works
|
||||
std::shared_ptr<json> schema = std::make_shared<json>(input);
|
||||
|
||||
// resolve all local schemas and references
|
||||
resolver r(*schema, id);
|
||||
do {
|
||||
// resolve all local schemas and references
|
||||
resolver r(*schema, id);
|
||||
|
||||
// check whether all undefined schema references can be resolved with existing ones
|
||||
std::set<json_uri> undefined;
|
||||
for (auto &ref : r.undefined_refs)
|
||||
if (schema_refs_.find(ref) == schema_refs_.end()) { // exact schema reference not found
|
||||
undefined.insert(ref);
|
||||
// check whether all undefined schema references can be resolved with existing ones
|
||||
std::set<json_uri> undefined;
|
||||
for (auto &ref : r.undefined_refs)
|
||||
if (schema_refs_.find(ref) == schema_refs_.end()) // exact schema reference not found
|
||||
undefined.insert(ref);
|
||||
|
||||
if (undefined.size() == 0) { // no undefined references
|
||||
// now insert all schema-references
|
||||
// check whether all schema-references are new
|
||||
for (auto &sref : r.schema_refs) {
|
||||
if (schema_refs_.find(sref.first) != schema_refs_.end())
|
||||
throw std::invalid_argument("schema " + sref.first.to_string() + " already present in validator.");
|
||||
}
|
||||
// no undefined references and no duplicated schema - store the schema
|
||||
schema_store_.push_back(schema);
|
||||
|
||||
// and insert all references
|
||||
schema_refs_.insert(r.schema_refs.begin(), r.schema_refs.end());
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// anything cannot be resolved, inform the user and make him/her load additional schemas
|
||||
// before retrying
|
||||
if (undefined.size() > 0)
|
||||
return undefined;
|
||||
if (schema_loader_ == nullptr)
|
||||
throw std::invalid_argument("schema contains undefined references to other schemas, needed schema-loader.");
|
||||
|
||||
// check whether all schema-references are new
|
||||
for (auto &sref : r.schema_refs) {
|
||||
if (schema_refs_.find(sref.first) != schema_refs_.end())
|
||||
throw std::invalid_argument("schema " + sref.first.to_string() + " already present in validator.");
|
||||
}
|
||||
for (auto undef : undefined) {
|
||||
json ext;
|
||||
|
||||
// no undefined references and no duplicated schema - store the schema
|
||||
schema_store_.push_back(schema);
|
||||
|
||||
// and insert all references
|
||||
schema_refs_.insert(r.schema_refs.begin(), r.schema_refs.end());
|
||||
schema_loader_(undef, ext);
|
||||
insert_schema(ext, undef.url());
|
||||
}
|
||||
} while (1);
|
||||
|
||||
// store the document root-schema
|
||||
if (id == json_uri("#"))
|
||||
root_schema_ = schema;
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
void json_validator::validate(json &instance)
|
||||
@ -318,10 +326,17 @@ void json_validator::validate(json &instance)
|
||||
validate(instance, *root_schema_, "root");
|
||||
}
|
||||
|
||||
json_validator::json_validator(const json &schema, std::function<void(const json_uri &, json &)> loader)
|
||||
: schema_loader_(loader)
|
||||
{
|
||||
insert_schema(schema, json_uri("#"));
|
||||
}
|
||||
|
||||
void json_validator::validate(json &instance, const json &schema_, const std::string &name)
|
||||
{
|
||||
const json *schema = &schema_;
|
||||
|
||||
// $ref resolution
|
||||
do {
|
||||
const auto &ref = schema->find("$ref");
|
||||
if (ref == schema->end())
|
||||
|
||||
Loading…
Reference in New Issue
Block a user