diff --git a/README.md b/README.md index ac55838..0edac4a 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,12 @@ lives in his namespace. 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) +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 The current state of the build-system needs at least version **3.1.1** of NLohmann's diff --git a/src/json-validator.cpp b/src/json-validator.cpp index ecb88da..7dfa7ab 100644 --- a/src/json-validator.cpp +++ b/src/json-validator.cpp @@ -186,64 +186,98 @@ void validate_boolean(const json & /*instance*/, const json &schema, const std:: validate_type(schema, "boolean", name); } -void validate_numeric(const json &schema, const std::string &name, double value) +template +bool violates_numeric_maximum(T max, T value, bool exclusive) { + if (exclusive) + return value > max; + + return value >= max; +} + +template +bool violates_numeric_minimum(T min, T value, bool exclusive) +{ + if (exclusive) + return value < min; + + return value <= min; +} + +template +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 const auto &multipleOf = schema.find("multipleOf"); if (multipleOf != schema.end()) { - if (multipleOf.value().get() != 0.0) { + T multiple = multipleOf.value(); + if (multiple != 0.0) { - double v = value; - v /= multipleOf.value().get(); - - if (v != (double) (long) v) - throw std::out_of_range(name + " is not a multiple ..."); + T v = value / multiple; + if (v != (T) (long) v) + throw std::out_of_range(name + " is not a multiple of " + std::to_string(multiple)); } } const auto &maximum = schema.find("maximum"); if (maximum != schema.end()) { - double maxi = maximum.value(); - auto ex = std::out_of_range(name + " exceeds maximum of " + std::to_string(maxi)); - if (schema.find("exclusiveMaximum") != schema.end()) { - if (value >= maxi) - throw ex; - } else { - if (value > maxi) - throw ex; - } + T maxi = maximum.value(); + + const auto &excl = schema.find("exclusiveMaximum"); + bool exclusive = (excl != schema.end()) ? excl.value().get() : false; + + if (violates_numeric_maximum(maxi, value, exclusive)) + throw std::out_of_range(name + " exceeds maximum of " + std::to_string(maxi)); } const auto &minimum = schema.find("minimum"); if (minimum != schema.end()) { - double 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()) { - if (value <= mini) - throw ex; - } else { - if (value < mini) - throw ex; - } + T mini = minimum.value(); + + const auto &excl = schema.find("exclusiveMinimum"); + bool exclusive = (excl != schema.end()) ? excl.value().get() : false; + + if (violates_numeric_minimum(mini, value, exclusive)) + throw std::out_of_range(name + " is below minimum of " + std::to_string(mini)); } } void validate_integer(const json &instance, const json &schema, const std::string &name) { validate_type(schema, "integer", name); - validate_numeric(schema, name, instance.get()); + //TODO: Validate schema values are json::value_t::number_integer/unsigned? + + validate_numeric(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) { validate_type(schema, "integer", name); - validate_numeric(schema, name, instance.get()); + //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(instance, schema, name); + else + validate_numeric(instance, schema, name); } void validate_float(const json &instance, const json &schema, const std::string &name) { validate_type(schema, "number", name); - validate_numeric(schema, name, instance.get()); + //TODO: Validate schema values are json::value_t::number_float? + + validate_numeric(instance, schema, name); } void validate_null(const json & /*instance*/, const json &schema, const std::string &name)