diff --git a/include/jsoncons_ext/jsonschema/common/keyword_validator.hpp b/include/jsoncons_ext/jsonschema/common/keyword_validator.hpp index 320f12df5..931f68f15 100644 --- a/include/jsoncons_ext/jsonschema/common/keyword_validator.hpp +++ b/include/jsoncons_ext/jsonschema/common/keyword_validator.hpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -92,26 +93,30 @@ namespace jsonschema { validation_message make_validation_message(const jsonpointer::json_pointer& eval_path, const jsonpointer::json_pointer& instance_location, - const std::string& message) const override + const std::string& message, + std::optional patch = std::nullopt) const override { return validation_message(keyword_name_, eval_path, schema_location_, instance_location, - custom_message_.empty() ? message : custom_message_); + custom_message_.empty() ? message : custom_message_, + patch); } validation_message make_validation_message(const jsonpointer::json_pointer& eval_path, const jsonpointer::json_pointer& instance_location, const std::string& message, - const std::vector& details) const override + const std::vector& details, + std::optional patch = std::nullopt) const override { return validation_message(keyword_name_, eval_path, schema_location_, instance_location, custom_message_.empty() ? message : custom_message_, - details); + details, + patch); } }; @@ -1999,7 +2004,7 @@ namespace jsonschema { const jsonpointer::json_pointer& instance_location, evaluation_results& /*results*/, error_reporter& reporter, - Json& /*patch*/) const final + Json& patch) const final { if (!instance.is_object()) { @@ -2015,7 +2020,8 @@ namespace jsonschema { walk_result result = reporter.error(this->make_validation_message( this_context.eval_path(), instance_location, - "Required property '" + key + "' not found.")); + "Required property '" + key + "' not found.", + &patch)); if(result == walk_result::abort) { return result; diff --git a/include/jsoncons_ext/jsonschema/common/validator.hpp b/include/jsoncons_ext/jsonschema/common/validator.hpp index 56b130f7b..64287e0eb 100644 --- a/include/jsoncons_ext/jsonschema/common/validator.hpp +++ b/include/jsoncons_ext/jsonschema/common/validator.hpp @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include @@ -239,12 +241,14 @@ namespace jsonschema { virtual validation_message make_validation_message(const jsonpointer::json_pointer& eval_path, const jsonpointer::json_pointer& instance_location, - const std::string& message) const = 0; + const std::string& message, + std::optional patch = std::nullopt) const = 0; virtual validation_message make_validation_message(const jsonpointer::json_pointer& eval_path, const jsonpointer::json_pointer& instance_location, const std::string& message, - const std::vector& details) const = 0; + const std::vector& details, + std::optional patch = std::nullopt) const = 0; }; } // namespace jsonschema diff --git a/include/jsoncons_ext/jsonschema/validation_message.hpp b/include/jsoncons_ext/jsonschema/validation_message.hpp index 7959c213f..ce5a0335a 100644 --- a/include/jsoncons_ext/jsonschema/validation_message.hpp +++ b/include/jsoncons_ext/jsonschema/validation_message.hpp @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include @@ -30,17 +32,20 @@ namespace jsonschema { jsonpointer::json_pointer instance_location_; std::string message_; std::vector details_; + std::optional patch_; public: validation_message(std::string keyword, jsonpointer::json_pointer eval_path, uri schema_location, jsonpointer::json_pointer instance_location, - std::string message) + std::string message, + std::optional patch = std::nullopt) : keyword_(std::move(keyword)), eval_path_(std::move(eval_path)), schema_location_(std::move(schema_location)), instance_location_(std::move(instance_location)), - message_(std::move(message)) + message_(std::move(message)), + patch_(patch) { } @@ -49,13 +54,15 @@ namespace jsonschema { const uri& schema_location, const jsonpointer::json_pointer& instance_location, const std::string& message, - const std::vector& details) + const std::vector& details, + std::optional patch = std::nullopt) : keyword_(keyword), eval_path_(eval_path), schema_location_(schema_location), instance_location_(instance_location), message_(message), - details_(details) + details_(details), + patch_(patch) { } @@ -88,6 +95,11 @@ namespace jsonschema { { return details_; } + + std::optional patch() const + { + return patch_; + } }; } // namespace jsonschema diff --git a/test/jsonschema/src/dynamic_ref_tests.cpp b/test/jsonschema/src/dynamic_ref_tests.cpp index 66ad7ae1e..f4d4f22d8 100644 --- a/test/jsonschema/src/dynamic_ref_tests.cpp +++ b/test/jsonschema/src/dynamic_ref_tests.cpp @@ -20,6 +20,119 @@ namespace jsonpatch = jsoncons::jsonpatch; TEST_CASE("jsonschema $recursiveRef tests") { + { + + std::cout << "**********************888\n"; + + std::string schema_str = R"( +{ + "$id": "https://example.com/arrays.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "A representation of a person, company, organization, or place", + "type": "object", + "properties": { + "fruits": { + "type": "array", + "items": { + "type": "string" + } + }, + "vegetables": { + "type": "array", + "items": { "$ref": "#/$defs/veggie" } + } + }, + "$defs": { + "veggie": { + "type": "object", + "required": [ "veggieName", "veggieLike" ], + "properties": { + "veggieName": { + "type": "string", + "description": "The name of the vegetable." + }, + "veggieLike": { + "type": "boolean", + "description": "Do I like this vegetable?" + } + } + } + } +} + )"; + + std::string data_str = R"( +{ + "fruits": [ "apple", "orange", "pear" ], + "vegetables": [ + { + "veggieName": "potato", + "veggieLike": true + }, + { + "veggieName": "carrot", + "veggieLike": false + }, + { + "veggieName": "Swiss Chard" + } + ] +} + )"; + + json schema = json::parse(schema_str); + jsonschema::json_schema compiled = jsonschema::make_json_schema(std::move(schema)); + json data = json::parse(data_str); + json patch; + + // reporter that patching + auto reporter = [](const jsonschema::validation_message& msg) -> jsonschema::walk_result + { + auto patchInside = msg.patch(); + if (patchInside.has_value()) + { + try { + json* patchPtr = std::any_cast(patchInside.value()); + + if (msg.message().find("Required property") != std::string::npos && msg.message().find("not found") != std::string::npos) + { + json j; + j.try_emplace("op", "add"); + j.try_emplace("path", msg.instance_location().string() + "/veggieLike"); + j.try_emplace("value", false); + patchPtr->push_back(std::move(j)); + + //there could be "return jsonschema::walk_result::advance_no_error;" for saying to validator "its not error, go forward!" + } + + } catch (const std::bad_any_cast& e) { + std::cout << "cant cast patch-any:" << e.what() << "\n"; + } + } + std::cout << msg.instance_location().string() << ": " << msg.message() << "\n"; + return jsonschema::walk_result::advance; + }; + compiled.validate(data, reporter, patch); + + std::cout << "\n(3) Validate outputting to a json decoder\n"; + jsoncons::json_decoder decoder; + compiled.validate(data, decoder); + json output = decoder.get_result(); + std::cout << pretty_print(output) << "\n"; + + std::cout << "PATCH:" << pretty_print(patch) << "\n"; + + jsoncons::jsonpatch::apply_patch(data, patch); + + compiled.validate(data, decoder); + + CHECK(decoder.is_valid()); +} + + + + + std::string tree_schema_str = R"( { "$schema": "https://json-schema.org/draft/2019-09/schema",