Description:
- Differentiate integer and floating point validation
- Correct exclusive minimum/maximum comparison operators
This commit is contained in:
Bryan Gillespie 2018-04-02 13:05:58 -04:00 committed by Bryan Gillespie
parent c552c7c02a
commit bffa46cb20
2 changed files with 68 additions and 28 deletions

View File

@ -34,6 +34,12 @@ lives in his namespace.
Schema-reference resolution is not recursivity-proven: If there is a nested Schema-reference resolution is not recursivity-proven: If there is a nested
cross-schema reference, it will not stop. (Though I haven't tested it) cross-schema reference, it will not stop. (Though I haven't tested it)
Numerical validation uses `int64_t`, `uint64_t` or `double`, depending on if the schema type is "integer" or "number". Bignum (i.e. arbitrary precision and range) is not supported at this time.
Unsigned integer validation will only take place if the following two conditions are true:
- The nlohmann `type()` of the json object under validation is `nlohmann::json::value_t::number_unsigned`
- The schema specifies a numerical minimum greater than or equal to 0
# How to use # How to use
The current state of the build-system needs at least version **3.1.1** of NLohmann's The current state of the build-system needs at least version **3.1.1** of NLohmann's

View File

@ -186,64 +186,98 @@ void validate_boolean(const json & /*instance*/, const json &schema, const std::
validate_type(schema, "boolean", name); validate_type(schema, "boolean", name);
} }
void validate_numeric(const json &schema, const std::string &name, double value) template <class T>
bool violates_numeric_maximum(T max, T value, bool exclusive)
{ {
if (exclusive)
return value > max;
return value >= max;
}
template <class T>
bool violates_numeric_minimum(T min, T value, bool exclusive)
{
if (exclusive)
return value < min;
return value <= min;
}
template <class T>
void validate_numeric(const json &instance, const json &schema, const std::string &name)
{
T value = instance;
// multipleOf - if the rest of the division is 0 -> OK // multipleOf - if the rest of the division is 0 -> OK
const auto &multipleOf = schema.find("multipleOf"); const auto &multipleOf = schema.find("multipleOf");
if (multipleOf != schema.end()) { if (multipleOf != schema.end()) {
if (multipleOf.value().get<double>() != 0.0) { T multiple = multipleOf.value();
if (multiple != 0.0) {
double v = value; T v = value / multiple;
v /= multipleOf.value().get<double>(); if (v != (T) (long) v)
throw std::out_of_range(name + " is not a multiple of " + std::to_string(multiple));
if (v != (double) (long) v)
throw std::out_of_range(name + " is not a multiple ...");
} }
} }
const auto &maximum = schema.find("maximum"); const auto &maximum = schema.find("maximum");
if (maximum != schema.end()) { if (maximum != schema.end()) {
double maxi = maximum.value(); T maxi = maximum.value();
auto ex = std::out_of_range(name + " exceeds maximum of " + std::to_string(maxi));
if (schema.find("exclusiveMaximum") != schema.end()) { const auto &excl = schema.find("exclusiveMaximum");
if (value >= maxi) bool exclusive = (excl != schema.end()) ? excl.value().get<bool>() : false;
throw ex;
} else { if (violates_numeric_maximum<T>(maxi, value, exclusive))
if (value > maxi) throw std::out_of_range(name + " exceeds maximum of " + std::to_string(maxi));
throw ex;
}
} }
const auto &minimum = schema.find("minimum"); const auto &minimum = schema.find("minimum");
if (minimum != schema.end()) { if (minimum != schema.end()) {
double mini = minimum.value(); T mini = minimum.value();
auto ex = std::out_of_range(name + " is below the minimum of " + std::to_string(mini));
if (schema.find("exclusiveMinimum") != schema.end()) { const auto &excl = schema.find("exclusiveMinimum");
if (value <= mini) bool exclusive = (excl != schema.end()) ? excl.value().get<bool>() : false;
throw ex;
} else { if (violates_numeric_minimum<T>(mini, value, exclusive))
if (value < mini) throw std::out_of_range(name + " is below minimum of " + std::to_string(mini));
throw ex;
}
} }
} }
void validate_integer(const json &instance, const json &schema, const std::string &name) void validate_integer(const json &instance, const json &schema, const std::string &name)
{ {
validate_type(schema, "integer", name); validate_type(schema, "integer", name);
validate_numeric(schema, name, instance.get<double>()); //TODO: Validate schema values are json::value_t::number_integer/unsigned?
validate_numeric<int64_t>(instance, schema, name);
}
bool is_unsigned(const json &schema)
{
const auto &minimum = schema.find("minimum");
// Number is expected to be unsigned if a minimum >= 0 is set
return minimum != schema.end() && minimum.value() >= 0;
} }
void validate_unsigned(const json &instance, const json &schema, const std::string &name) void validate_unsigned(const json &instance, const json &schema, const std::string &name)
{ {
validate_type(schema, "integer", name); validate_type(schema, "integer", name);
validate_numeric(schema, name, instance.get<double>()); //TODO: Validate schema values are json::value_t::unsigned?
//Is there a better way to determine whether an unsigned comparison should take place?
if (is_unsigned(schema))
validate_numeric<uint64_t>(instance, schema, name);
else
validate_numeric<int64_t>(instance, schema, name);
} }
void validate_float(const json &instance, const json &schema, const std::string &name) void validate_float(const json &instance, const json &schema, const std::string &name)
{ {
validate_type(schema, "number", name); validate_type(schema, "number", name);
validate_numeric(schema, name, instance.get<double>()); //TODO: Validate schema values are json::value_t::number_float?
validate_numeric<double>(instance, schema, name);
} }
void validate_null(const json & /*instance*/, const json &schema, const std::string &name) void validate_null(const json & /*instance*/, const json &schema, const std::string &name)