From 40f5d293faf3615132711e8da2e9585c61a07fa6 Mon Sep 17 00:00:00 2001
From: Patrick Boettcher
Date: Fri, 10 Dec 2021 16:16:12 +0100
Subject: [PATCH] make default-values work for stacked $refs
---
app/json-schema-validate.cpp | 2 +-
src/json-validator.cpp | 71 ++++++++++++++-----------------
test/issue-189-default-values.cpp | 2 +-
3 files changed, 33 insertions(+), 42 deletions(-)
diff --git a/app/json-schema-validate.cpp b/app/json-schema-validate.cpp
index 4b51237..de683ff 100644
--- a/app/json-schema-validate.cpp
+++ b/app/json-schema-validate.cpp
@@ -65,7 +65,7 @@ int main(int argc, char *argv[])
// 2) create the validator and
json_validator validator(loader,
- nlohmann::json_schema::default_string_format_check);
+ nlohmann::json_schema::default_string_format_check);
try {
// insert this schema as the root to the validator
diff --git a/src/json-validator.cpp b/src/json-validator.cpp
index a0474fb..8229655 100644
--- a/src/json-validator.cpp
+++ b/src/json-validator.cpp
@@ -34,12 +34,11 @@ using namespace nlohmann::json_schema;
namespace
{
-static const json EmptyDefault = nullptr;
-
class schema
{
protected:
root_schema *root_;
+ json default_value_ = nullptr;
public:
virtual ~schema() = default;
@@ -49,11 +48,13 @@ public:
virtual void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const = 0;
- virtual const json &defaultValue(const json::json_pointer &, const json &, error_handler &) const
+ virtual const json &default_value(const json::json_pointer &, const json &, error_handler &) const
{
- return EmptyDefault;
+ return default_value_;
}
+ void set_default_value(const json &v) { default_value_ = v; }
+
static std::shared_ptr make(json &schema,
root_schema *root,
const std::vector &key,
@@ -75,16 +76,18 @@ class schema_ref : public schema
e.error(ptr, instance, "unresolved or freed schema-reference " + id_);
}
- const json &defaultValue(const json::json_pointer &ptr, const json &instance, error_handler &e) const override
+ const json &default_value(const json::json_pointer &ptr, const json &instance, error_handler &e) const override final
{
+ if (!default_value_.is_null())
+ return default_value_;
+
auto target = target_.lock();
-
if (target)
- return target->defaultValue(ptr, instance, e);
- else
- e.error(ptr, instance, "unresolved or freed schema-reference " + id_);
+ return target->default_value(ptr, instance, e);
- return EmptyDefault;
+ e.error(ptr, instance, "unresolved or freed schema-reference " + id_);
+
+ return default_value_;
}
public:
@@ -347,9 +350,9 @@ class logical_not : public schema
e.error(ptr, instance, "the subschema has succeeded, but it is required to not validate");
}
- const json &defaultValue(const json::json_pointer &ptr, const json &instance, error_handler &e) const override
+ const json &default_value(const json::json_pointer &ptr, const json &instance, error_handler &e) const override
{
- return subschema_->defaultValue(ptr, instance, e);
+ return subschema_->default_value(ptr, instance, e);
}
public:
@@ -443,7 +446,6 @@ bool logical_combination::is_validate_complete(const json &instance, cons
class type_schema : public schema
{
- json defaultValue_ = EmptyDefault;
std::vector> type_;
std::pair enum_, const_;
std::vector> logic_;
@@ -456,11 +458,6 @@ class type_schema : public schema
std::shared_ptr if_, then_, else_;
- const json &defaultValue(const json::json_pointer &, const json &, error_handler &) const override
- {
- return defaultValue_;
- }
-
void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const override final
{
// depending on the type of instance run the type specific validator - if present
@@ -551,9 +548,10 @@ public:
sch.erase(attr);
}
- const auto default_attr = sch.find("default");
- if (default_attr != sch.end()) {
- defaultValue_ = default_attr.value();
+ attr = sch.find("default");
+ if (attr != sch.end()) {
+ set_default_value(attr.value());
+ sch.erase(attr);
}
for (auto &key : known_keywords)
@@ -630,11 +628,6 @@ public:
sch.erase(attr);
}
}
-
- void set_default_value(const json &default_value)
- {
- defaultValue_ = default_value;
- }
};
class string : public schema
@@ -986,9 +979,9 @@ class object : public schema
for (auto const &prop : properties_) {
const auto finding = instance.find(prop.first);
if (instance.end() == finding) { // if the prop is not in the instance
- const auto &defaultValue = prop.second->defaultValue(ptr, instance, e);
- if (!defaultValue.is_null()) { // if default value is available
- patch.add((ptr / prop.first), defaultValue);
+ const auto &default_value = prop.second->default_value(ptr, instance, e);
+ if (!default_value.is_null()) { // if default value is available
+ patch.add((ptr / prop.first), default_value);
}
}
}
@@ -1282,23 +1275,21 @@ std::shared_ptr schema::make(json &schema,
auto id = uris.back().derive(attr.value().get());
sch = root->get_or_create_ref(id);
- const auto default_attr = schema.find("default");
- if (default_attr != schema.end()) {
- if (type_schema *type_sch = dynamic_cast(sch.get())) {
- // copy the referenced schema and modify the default value
- auto schema_copy = std::make_shared(*type_sch);
- schema_copy->set_default_value(default_attr.value());
- sch = schema_copy;
- }
- }
-
schema.erase(attr);
+
+ // special case where break draft-7 and allow overriding of properties when a $ref is used
+ attr = schema.find("default");
+ if (attr != schema.end()) {
+ // copy the referenced schema and modify the default value
+ sch = std::make_shared(*dynamic_cast(sch.get()));
+ sch->set_default_value(attr.value());
+ schema.erase(attr);
+ }
} else {
sch = std::make_shared(schema, root, uris);
}
schema.erase("$schema");
- schema.erase("default");
schema.erase("title");
schema.erase("description");
} else {
diff --git a/test/issue-189-default-values.cpp b/test/issue-189-default-values.cpp
index 94ec2db..606e953 100644
--- a/test/issue-189-default-values.cpp
+++ b/test/issue-189-default-values.cpp
@@ -16,7 +16,7 @@ static const json quad_schema = R"(
"height": {
"$ref": "#/definitions/length"
},
- "depths": {
+ "depth": {
"$ref": "default_schema#/definitions/defaultLength"
}
},