From 42a8b00d1d8b55b5387ab61a2e4fe531d9c0a2d4 Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Fri, 23 Dec 2016 14:34:40 +0100 Subject: [PATCH] validator: $ref implemented for local references But needs more work. --- README.md | 2 +- app/json-schema-test.cpp | 8 +-- app/json-schema-validate.cpp | 5 +- src/json-schema-validator.hpp | 91 +++++++++++++++++++++++++++-------- 4 files changed, 80 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 64aefc3..1a432b2 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ int main(void) There is an application which can be used for testing the validator with the [JSON-Schema-Test-Suite](https://github.com/json-schema-org/JSON-Schema-Test-Suite). -Currently **93** of ~**308** tests are still failing, because simply not all keywords and +Currently **82** of ~**308** tests are still failing, because simply not all keywords and their functionalities have been implemented. Some of the missing feature will require a rework. Some will only work with external libraries. (remote references) diff --git a/app/json-schema-test.cpp b/app/json-schema-test.cpp index ac1daf4..8a91a3d 100644 --- a/app/json-schema-test.cpp +++ b/app/json-schema-test.cpp @@ -14,8 +14,6 @@ int main(void) return EXIT_FAILURE; } - json_validator validator; - size_t total_failed = 0, total = 0; @@ -33,7 +31,9 @@ int main(void) bool valid = true; try { - validator.validate(test_case["data"], schema); + json_validator validator; + validator.set_schema("#", schema); + 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"; @@ -62,7 +62,7 @@ int main(void) std::cout << "-------------\n"; } - std::cout << (total - total_failed) << " of " << total << " have succeeded - " << total_failed << " failed\n"; + std::cout << "Total RESULT: " << (total - total_failed) << " of " << total << " have succeeded - " << total_failed << " failed\n"; return total_failed; } diff --git a/app/json-schema-validate.cpp b/app/json-schema-validate.cpp index 3cd69a4..5eb5aaa 100644 --- a/app/json-schema-validate.cpp +++ b/app/json-schema-validate.cpp @@ -42,10 +42,11 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - json_validator validator; try { - validator.validate(document, schema); + json_validator validator; + validator.set_schema("#", schema); + validator.validate(document); } catch (std::exception &e) { std::cerr << "schema validation failed\n"; std::cerr << e.what() << "\n"; diff --git a/src/json-schema-validator.hpp b/src/json-schema-validator.hpp index 234420f..1b2c54f 100644 --- a/src/json-schema-validator.hpp +++ b/src/json-schema-validator.hpp @@ -314,6 +314,7 @@ class json_validator additionalProperties = Object; } + // patternProperties json patternProperties = {}; if (schema.find("patternProperties") != schema.end()) patternProperties = schema["patternProperties"]; @@ -400,57 +401,109 @@ class json_validator } } -public: - void validate(json &instance, const json &schema, const std::string &name = "root") + void validate(json &instance, const json &schema_, const std::string &name) { - not_yet_implemented(schema, "allOf", "all"); - not_yet_implemented(schema, "anyOf", "all"); - not_yet_implemented(schema, "oneOf", "all"); - not_yet_implemented(schema, "not", "all"); - not_yet_implemented(schema, "definitions", "all"); - not_yet_implemented(schema, "$ref", "all"); + not_yet_implemented(schema_, "allOf", "all"); + not_yet_implemented(schema_, "anyOf", "all"); + not_yet_implemented(schema_, "oneOf", "all"); + not_yet_implemented(schema_, "not", "all"); - validate_enum(instance, schema, name); +// std::cerr << instance << " VS\n"; +// std::cerr << schema_ << "\n"; +// std::cerr << "\n"; + + const json *schema = &schema_; + + do { + const auto &ref = schema->find("$ref"); + if (ref != schema->end()) { + std::string r = ref.value(); + + // do we have stored a schema which correspond to this reference + if (schema_references.find(r) == schema_references.end()) { // no + + if (r[0] != '#') + throw std::logic_error("remote references are not yet implemented for ref " + r); + + schema = schema_references["#"]; // root schema + + // Aieee, need so much better parsing than this TODO + r = r.substr(1); // skip '#' + + std::regex re("\\/([a-zA-Z0-9~%]+)"); + for (auto match = std::sregex_iterator(r.begin(), r.end(), re), end = std::sregex_iterator(); + match != end; + ++match) { + + std::string name = match->str().substr(1); + + const auto &sub = schema->find(name); + if (sub == schema->end()) + throw std::invalid_argument("reference schema " + r + " not found\n"); + + schema = &(*sub); + } + + schema_references[r] = schema; + } else + schema = schema_references[r]; + } else + break; + } while (1); // loop in case of nested refs + + validate_enum(instance, *schema, name); switch (instance.type()) { case json::value_t::object: - validate_object(instance, schema, name); + validate_object(instance, *schema, name); break; case json::value_t::array: - validate_array(instance, schema, name); + validate_array(instance, *schema, name); break; case json::value_t::string: - validate_string(instance, schema, name); + validate_string(instance, *schema, name); break; case json::value_t::number_unsigned: - validate_unsigned(instance, schema, name); + validate_unsigned(instance, *schema, name); break; case json::value_t::number_integer: - validate_integer(instance, schema, name); + validate_integer(instance, *schema, name); break; case json::value_t::number_float: - validate_float(instance, schema, name); + validate_float(instance, *schema, name); break; case json::value_t::boolean: - validate_boolean(instance, schema, name); + validate_boolean(instance, *schema, name); break; case json::value_t::null: - validate_null(instance, schema, name); + validate_null(instance, *schema, name); break; default: - throw std::out_of_range("type '" + schema["type"].get() + - "' has no validator yet"); + assert(0 && "unexpected instance type for validation"); break; } } + +public: + std::map schema_references; + + void set_schema(const std::string &ref, const json &schema) + { + schema_references[ref] = &schema; /* replace or insert */ + } + + void validate(json &instance) + { + validate(instance, *schema_references["#"], "root"); + } }; }