ref-schema: create a new ref-schema when default-value is present

Default-values on schemas with a $ref field are now stored within
a new reference schema which links to the original reference schema.
It contains the default value and keeps a strong reference (shared_ptr)
to the original reference.

Fixes #209
This commit is contained in:
Patrick Boettcher 2022-06-27 10:30:11 +02:00
parent c6cb3d4c2d
commit 4f67636760
5 changed files with 54 additions and 5 deletions

View File

@ -65,6 +65,8 @@ class schema_ref : public schema
{ {
const std::string id_; const std::string id_;
std::weak_ptr<schema> target_; std::weak_ptr<schema> target_;
std::shared_ptr<schema> target_strong_; // for references to references keep also the shared_ptr because
// no one else might use it after resolving
void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const final void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const final
{ {
@ -95,7 +97,13 @@ public:
: schema(root), id_(id) {} : schema(root), id_(id) {}
const std::string &id() const { return id_; } const std::string &id() const { return id_; }
void set_target(const std::shared_ptr<schema> &target) { target_ = target; }
void set_target(const std::shared_ptr<schema> &target, bool strong = false)
{
target_ = target;
if (strong)
target_strong_ = target;
}
}; };
} // namespace } // namespace
@ -1277,13 +1285,17 @@ std::shared_ptr<schema> schema::make(json &schema,
schema.erase(attr); schema.erase(attr);
// special case where break draft-7 and allow overriding of properties when a $ref is used // special case where we break draft-7 and allow overriding of properties when a $ref is used
attr = schema.find("default"); attr = schema.find("default");
if (attr != schema.end()) { if (attr != schema.end()) {
// copy the referenced schema depending on the underlying type and modify the default value // copy the referenced schema depending on the underlying type and modify the default value
if (auto *ref_sch = dynamic_cast<schema_ref *>(sch.get())) { if (dynamic_cast<schema_ref *>(sch.get())) {
sch = std::make_shared<schema_ref>(*ref_sch); // create a new reference schema use the original reference (which will be resolved later)
sch->set_default_value(attr.value()); // to store this overloaed default value #209
auto overloaded_ref_sch = std::make_shared<schema_ref>(uris[0].to_string(), root);
overloaded_ref_sch->set_target(sch, true);
overloaded_ref_sch->set_default_value(attr.value());
sch = overloaded_ref_sch;
} else if (auto *type_sch = dynamic_cast<type_schema *>(sch.get())) { } else if (auto *type_sch = dynamic_cast<type_schema *>(sch.get())) {
sch = std::make_shared<type_schema>(*type_sch); sch = std::make_shared<type_schema>(*type_sch);
sch->set_default_value(attr.value()); sch->set_default_value(attr.value());

View File

@ -0,0 +1,3 @@
add_test_simple_schema(Issue::209
${CMAKE_CURRENT_SOURCE_DIR}/entities.schema.json
${CMAKE_CURRENT_SOURCE_DIR}/instance.json)

View File

@ -0,0 +1,8 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://example.invalid/color.schema.json",
"title": "color",
"description": "X11/HTML/CSS color name as a JSON string",
"type": "string",
"enum": [ "White", "Black", "Red" ]
}

View File

@ -0,0 +1,16 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://example.invalid/entities.schema.json",
"title": "Entities",
"type": "array",
"items": {
"type": "object",
"required": [ "name" ],
"properties": {
"name": {
"type": "string"
},
"fg": { "$ref": "color.schema.json", "default": "Black" }
}
}
}

View File

@ -0,0 +1,10 @@
[
{
"name": "player",
"fg": "White"
},
{
"name": "enemy",
"fg": "Red"
}
]