validator: $ref implemented for local references

But needs more work.
This commit is contained in:
Patrick Boettcher 2016-12-23 14:34:40 +01:00
parent 0789403a4b
commit 42a8b00d1d
4 changed files with 80 additions and 26 deletions

View File

@ -84,7 +84,7 @@ int main(void)
There is an application which can be used for testing the validator with the 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). [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 their functionalities have been implemented. Some of the missing feature will
require a rework. Some will only work with external libraries. (remote references) require a rework. Some will only work with external libraries. (remote references)

View File

@ -14,8 +14,6 @@ int main(void)
return EXIT_FAILURE; return EXIT_FAILURE;
} }
json_validator validator;
size_t total_failed = 0, size_t total_failed = 0,
total = 0; total = 0;
@ -33,7 +31,9 @@ int main(void)
bool valid = true; bool valid = true;
try { 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) { } catch (const std::out_of_range &e) {
valid = false; valid = false;
std::cout << " Test Case Exception (out of range): " << e.what() << "\n"; std::cout << " Test Case Exception (out of range): " << e.what() << "\n";
@ -62,7 +62,7 @@ int main(void)
std::cout << "-------------\n"; 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; return total_failed;
} }

View File

@ -42,10 +42,11 @@ int main(int argc, char *argv[])
return EXIT_FAILURE; return EXIT_FAILURE;
} }
json_validator validator;
try { try {
validator.validate(document, schema); json_validator validator;
validator.set_schema("#", schema);
validator.validate(document);
} catch (std::exception &e) { } catch (std::exception &e) {
std::cerr << "schema validation failed\n"; std::cerr << "schema validation failed\n";
std::cerr << e.what() << "\n"; std::cerr << e.what() << "\n";

View File

@ -314,6 +314,7 @@ class json_validator
additionalProperties = Object; additionalProperties = Object;
} }
// patternProperties
json patternProperties = {}; json patternProperties = {};
if (schema.find("patternProperties") != schema.end()) if (schema.find("patternProperties") != schema.end())
patternProperties = schema["patternProperties"]; patternProperties = schema["patternProperties"];
@ -400,57 +401,109 @@ class json_validator
} }
} }
public: void validate(json &instance, const json &schema_, const std::string &name)
void validate(json &instance, const json &schema, const std::string &name = "root")
{ {
not_yet_implemented(schema, "allOf", "all"); not_yet_implemented(schema_, "allOf", "all");
not_yet_implemented(schema, "anyOf", "all"); not_yet_implemented(schema_, "anyOf", "all");
not_yet_implemented(schema, "oneOf", "all"); not_yet_implemented(schema_, "oneOf", "all");
not_yet_implemented(schema, "not", "all"); not_yet_implemented(schema_, "not", "all");
not_yet_implemented(schema, "definitions", "all");
not_yet_implemented(schema, "$ref", "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()) { switch (instance.type()) {
case json::value_t::object: case json::value_t::object:
validate_object(instance, schema, name); validate_object(instance, *schema, name);
break; break;
case json::value_t::array: case json::value_t::array:
validate_array(instance, schema, name); validate_array(instance, *schema, name);
break; break;
case json::value_t::string: case json::value_t::string:
validate_string(instance, schema, name); validate_string(instance, *schema, name);
break; break;
case json::value_t::number_unsigned: case json::value_t::number_unsigned:
validate_unsigned(instance, schema, name); validate_unsigned(instance, *schema, name);
break; break;
case json::value_t::number_integer: case json::value_t::number_integer:
validate_integer(instance, schema, name); validate_integer(instance, *schema, name);
break; break;
case json::value_t::number_float: case json::value_t::number_float:
validate_float(instance, schema, name); validate_float(instance, *schema, name);
break; break;
case json::value_t::boolean: case json::value_t::boolean:
validate_boolean(instance, schema, name); validate_boolean(instance, *schema, name);
break; break;
case json::value_t::null: case json::value_t::null:
validate_null(instance, schema, name); validate_null(instance, *schema, name);
break; break;
default: default:
throw std::out_of_range("type '" + schema["type"].get<std::string>() + assert(0 && "unexpected instance type for validation");
"' has no validator yet");
break; break;
} }
} }
public:
std::map<std::string, const json *> 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");
}
}; };
} }