validator: implement allOf, oneOf, anyOf and not logic
This commit is contained in:
parent
6c0620a90b
commit
f725e296cc
@ -31,7 +31,8 @@
|
|||||||
using nlohmann::json;
|
using nlohmann::json;
|
||||||
using nlohmann::json_uri;
|
using nlohmann::json_uri;
|
||||||
|
|
||||||
namespace {
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
class resolver
|
class resolver
|
||||||
{
|
{
|
||||||
@ -57,7 +58,7 @@ class resolver
|
|||||||
switch (i.value().type()) {
|
switch (i.value().type()) {
|
||||||
|
|
||||||
case json::value_t::object: // child is object, it is a schema
|
case json::value_t::object: // child is object, it is a schema
|
||||||
resolve(i.value(), id.append( json_uri::escape(i.key())) );
|
resolve(i.value(), id.append(json_uri::escape(i.key())));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case json::value_t::array: {
|
case json::value_t::array: {
|
||||||
@ -65,7 +66,7 @@ class resolver
|
|||||||
auto child_id = id.append(json_uri::escape(i.key()));
|
auto child_id = id.append(json_uri::escape(i.key()));
|
||||||
for (auto &v : i.value()) {
|
for (auto &v : i.value()) {
|
||||||
if (v.type() == json::value_t::object) // array element is object
|
if (v.type() == json::value_t::object) // array element is object
|
||||||
resolve(v, child_id.append(std::to_string(index)) );
|
resolve(v, child_id.append(std::to_string(index)));
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
@ -119,7 +120,8 @@ public:
|
|||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
namespace nlohmann {
|
namespace nlohmann
|
||||||
|
{
|
||||||
namespace json_schema_draft4
|
namespace json_schema_draft4
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -542,22 +544,82 @@ void json_validator::validate(json &instance, const json &schema_, const std::st
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
const auto &ref = schema->find("$ref");
|
const auto &ref = schema->find("$ref");
|
||||||
if (ref != schema->end()) {
|
if (ref == schema->end())
|
||||||
|
break;
|
||||||
|
|
||||||
auto it = schema_refs_.find(ref.value());
|
auto it = schema_refs_.find(ref.value());
|
||||||
|
|
||||||
if (it == schema_refs_.end())
|
if (it == schema_refs_.end())
|
||||||
throw std::invalid_argument("schema reference " + ref.value().get<std::string>() + " not found. Make sure all schemas have been inserted before validation.");
|
throw std::invalid_argument("schema reference " + ref.value().get<std::string>() + " not found. Make sure all schemas have been inserted before validation.");
|
||||||
|
|
||||||
schema = it->second;
|
schema = it->second;
|
||||||
} else
|
|
||||||
break;
|
|
||||||
} while (1); // loop in case of nested refs
|
} while (1); // loop in case of nested refs
|
||||||
|
|
||||||
not_yet_implemented(*schema, "allOf", "all");
|
// not
|
||||||
not_yet_implemented(*schema, "anyOf", "all");
|
const auto attr = schema->find("not");
|
||||||
not_yet_implemented(*schema, "oneOf", "all");
|
if (attr != schema->end()) {
|
||||||
not_yet_implemented(*schema, "not", "all");
|
bool ok;
|
||||||
|
|
||||||
|
try {
|
||||||
|
validate(instance, attr.value(), name);
|
||||||
|
ok = false;
|
||||||
|
} catch (std::exception &e) {
|
||||||
|
ok = true;
|
||||||
|
}
|
||||||
|
if (!ok)
|
||||||
|
throw std::invalid_argument("schema match for " + name + " but a not-match is defined by schema.");
|
||||||
|
return; // return here - not cannot be mixed with based-schemas?
|
||||||
|
}
|
||||||
|
|
||||||
|
// allOf, anyOf, oneOf
|
||||||
|
const json *combined_schemas = nullptr;
|
||||||
|
enum {
|
||||||
|
none,
|
||||||
|
allOf,
|
||||||
|
anyOf,
|
||||||
|
oneOf
|
||||||
|
} combine_logic = none;
|
||||||
|
|
||||||
|
{
|
||||||
|
const auto &attr = schema->find("allOf");
|
||||||
|
if (attr != schema->end()) {
|
||||||
|
combine_logic = allOf;
|
||||||
|
combined_schemas = &attr.value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const auto &attr = schema->find("anyOf");
|
||||||
|
if (attr != schema->end()) {
|
||||||
|
combine_logic = anyOf;
|
||||||
|
combined_schemas = &attr.value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const auto &attr = schema->find("oneOf");
|
||||||
|
if (attr != schema->end()) {
|
||||||
|
combine_logic = oneOf;
|
||||||
|
combined_schemas = &attr.value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (combine_logic != none) {
|
||||||
|
std::size_t count = 0;
|
||||||
|
for (const auto &s : *combined_schemas) {
|
||||||
|
try {
|
||||||
|
validate(instance, s, name);
|
||||||
|
count++;
|
||||||
|
} catch (std::exception &e) {
|
||||||
|
if (combine_logic == allOf)
|
||||||
|
throw std::out_of_range("At least one schema has failed for " + name + " where allOf them were requested.");
|
||||||
|
}
|
||||||
|
if (combine_logic == oneOf && count > 1)
|
||||||
|
throw std::out_of_range("More than one schema has succeeded for " + name + " where only oneOf them was requested.");
|
||||||
|
}
|
||||||
|
if ((combine_logic == anyOf || combine_logic == oneOf) && count == 0)
|
||||||
|
throw std::out_of_range("No schema has succeeded for " + name + " but anyOf/oneOf them should have worked.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// check (base) schema
|
||||||
validate_enum(instance, *schema, name);
|
validate_enum(instance, *schema, name);
|
||||||
|
|
||||||
switch (instance.type()) {
|
switch (instance.type()) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user