Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions include/jsoncons_ext/jsonschema/common/keyword_validator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <unordered_set>
#include <utility>
#include <vector>
#include <optional>

#include <jsoncons/config/jsoncons_config.hpp>
#include <jsoncons/conv_error.hpp>
Expand Down Expand Up @@ -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<std::any> 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<validation_message>& details) const override
const std::vector<validation_message>& details,
std::optional<std::any> 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);
}
};

Expand Down Expand Up @@ -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())
{
Expand All @@ -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;
Expand Down
8 changes: 6 additions & 2 deletions include/jsoncons_ext/jsonschema/common/validator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#include <string>
#include <unordered_set>
#include <vector>
#include <optional>
#include <any>

#include <jsoncons/config/jsoncons_config.hpp>
#include <jsoncons/utility/uri.hpp>
Expand Down Expand Up @@ -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<std::any> 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<validation_message>& details) const = 0;
const std::vector<validation_message>& details,
std::optional<std::any> patch = std::nullopt) const = 0;
};

} // namespace jsonschema
Expand Down
20 changes: 16 additions & 4 deletions include/jsoncons_ext/jsonschema/validation_message.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
#include <string>
#include <unordered_set>
#include <vector>
#include <optional>
#include <any>

#include <jsoncons/config/jsoncons_config.hpp>
#include <jsoncons/utility/uri.hpp>
Expand All @@ -30,17 +32,20 @@ namespace jsonschema {
jsonpointer::json_pointer instance_location_;
std::string message_;
std::vector<validation_message> details_;
std::optional<std::any> 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<std::any> 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)
{
}

Expand All @@ -49,13 +54,15 @@ namespace jsonschema {
const uri& schema_location,
const jsonpointer::json_pointer& instance_location,
const std::string& message,
const std::vector<validation_message>& details)
const std::vector<validation_message>& details,
std::optional<std::any> 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)
{
}

Expand Down Expand Up @@ -88,6 +95,11 @@ namespace jsonschema {
{
return details_;
}

std::optional<std::any> patch() const
{
return patch_;
}
};

} // namespace jsonschema
Expand Down
113 changes: 113 additions & 0 deletions test/jsonschema/src/dynamic_ref_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<json> 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<json*>(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<json> 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<json>(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",
Expand Down
Loading