Merge remote-tracking branch 'origin/master' into json_pointer-pop-push

This commit is contained in:
garethsb-sony 2019-02-01 09:18:26 +00:00
commit 11b188cfef
10 changed files with 97 additions and 75 deletions

View File

@ -60,7 +60,7 @@ script:
- $CXX --version
# put json.hpp to nlohmann
- mkdir -p nlohmann && wget https://github.com/nlohmann/json/releases/download/v3.5.0/json.hpp -O nlohmann/json.hpp
- mkdir -p nlohmann && wget https://github.com/nlohmann/json/releases/download/v3.6.0/json.hpp -O nlohmann/json.hpp
# compile and execute unit tests
- mkdir -p build && cd build

View File

@ -46,7 +46,8 @@ set_target_properties(json-schema-validator
install(TARGETS json-schema-validator
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib)
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin)
install(DIRECTORY src/
DESTINATION include
@ -59,12 +60,13 @@ target_include_directories(json-schema-validator
target_compile_features(json-schema-validator
PUBLIC
cxx_range_for) # for C++11 - flags
# Enable more compiler warnings, except when using Visual Studio compiler
if(NOT MSVC)
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
target_compile_options(json-schema-validator
PRIVATE
-Wall -Wextra)
endif()
target_link_libraries(json-schema-validator
PUBLIC
json-hpp)

View File

@ -10,7 +10,7 @@ This is a C++ library for validating JSON documents based on a
[draft-7 of JSON Schema Validation](http://json-schema.org/schema).
First a disclaimer: *It is work in progress and
contributions or hints or discussions are welcome.* Even though a 2.0.0 release is immenent.
contributions or hints or discussions are welcome.* Even though a 2.0.0 release is imminent.
Niels Lohmann et al develop a great JSON parser for C++ called [JSON for Modern
C++](https://github.com/nlohmann/json). This validator is based on this
@ -21,7 +21,7 @@ is rather simple.
# New in version 2
Although significant changes have been coorporate to the 2 version
Although significant changes have been done for the 2nd version
(a complete rewrite) the API is compatible with the 1.0.0 release. Except for
the namespace which is now `nlohmann::json_schema.
@ -29,7 +29,7 @@ Version **2** supports JSON schema draft 7, whereas 1 was supporting draft 4
only. Please update your schemas.
The primary change in 2 is the way a schema is used. While in version 1 the schema was
kept as a JSON-document and used again and again during validation, in versin 2 the schema
kept as a JSON-document and used again and again during validation, in version 2 the schema
is parsed into compiled C++ objects which are then used during validation. There are surely
still optimizations to be done, but validation speed has improved by factor 100
or more.
@ -42,9 +42,9 @@ messages if a JSON-document/instance does not comply to its schema.
By default this is done with exceptions thrown at the users with a helpful
message telling what's wrong with the document while validating.
With **2.0.0** the user can pass a `json_scheam::basic_error_handler` derived object
along with the instance to validate to receive a each time a validation error occurs
and decice what to do (throwing, counting, collecting).
With **2.0.0** the user can pass a `json_scheam::basic_error_handler`-derived
object along with the instance to validate to receive a callback each time a
validation error occurs and decide what to do (throwing, counting, collecting).
Another goal was to use Niels Lohmann's JSON-library. This is why the validator
lives in his namespace.
@ -55,12 +55,12 @@ Numerical validation uses nlohmann integer, unsigned and floating point types, d
the schema type is "integer" or "number". Bignum (i.e. arbitrary precision and
range) is not supported at this time.
Currently JSON-URI with "plain name fragments" are not supported. So referring to an URI
Currently JSON-URI with "plain name fragments" are not supported: referring to an URI
with `$ref: "file.json#plain"` will not work.
# How to use
The current state of the build-system needs at least version **3.5.0** of NLohmann's
The current state of the build-system needs at least version **3.6.0** of NLohmann's
JSON library. It is looking for the `json.hpp` within a `nlohmann/`-path.
When build the library you need to provide the path to the directory where the include-file
@ -167,7 +167,7 @@ int main()
std::cout << "About to validate this person:\n"
<< std::setw(2) << person << std::endl;
try {
validator.validate(person); // validate the document
validator.validate(person); // validate the document - uses the default throwing error-handler
std::cout << "Validation succeeded\n";
} catch (const std::exception &e) {
std::cerr << "Validation failed, here is why: " << e.what() << "\n";
@ -190,7 +190,7 @@ int main()
<< std::setw(2) << person << std::endl;
custom_error_handler err;
validator.validate(person, err); // validate the document - uses the default throwing error-handler
validator.validate(person, err); // validate the document
if (err)
std::cerr << "Validation failed\n";

View File

@ -55,7 +55,7 @@ int main()
std::cout << "About to validate this person:\n"
<< std::setw(2) << person << std::endl;
try {
validator.validate(person); // validate the document
validator.validate(person); // validate the document - uses the default throwing error-handler
std::cout << "Validation succeeded\n";
} catch (const std::exception &e) {
std::cerr << "Validation failed, here is why: " << e.what() << "\n";
@ -78,7 +78,7 @@ int main()
<< std::setw(2) << person << std::endl;
custom_error_handler err;
validator.validate(person, err); // validate the document - uses the default throwing error-handler
validator.validate(person, err); // validate the document
if (err)
std::cerr << "Validation failed\n";

View File

@ -24,11 +24,11 @@
#include <nlohmann/json.hpp>
#ifdef NLOHMANN_JSON_VERSION_MAJOR
# if NLOHMANN_JSON_VERSION_MAJOR < 3 || NLOHMANN_JSON_VERSION_MINOR < 5 || NLOHMANN_JSON_VERSION_PATCH < 1
# error "Please use this library with NLohmann's JSON version 3.5.1 or higher"
# if (NLOHMANN_JSON_VERSION_MAJOR * 10000 + NLOHMANN_JSON_VERSION_MINOR * 100 + NLOHMANN_JSON_VERSION_PATCH) < 30600
# error "Please use this library with NLohmann's JSON version 3.6.0 or higher"
# endif
#else
# error "expected existing NLOHMANN_JSON_VERSION_MAJOR preproc variable, please update to NLohmann's JSON 3.5.1"
# error "expected existing NLOHMANN_JSON_VERSION_MAJOR preproc variable, please update to NLohmann's JSON 3.6.0"
#endif
// make yourself a home - welcome to nlohmann's namespace
@ -51,7 +51,7 @@ class JSON_SCHEMA_VALIDATOR_API json_uri
std::string proto_;
std::string hostname_;
std::string path_;
nlohmann::json::json_pointer pointer_;
json::json_pointer pointer_;
protected:
// decodes a JSON uri and replaces all or part of the currently stored values
@ -72,7 +72,7 @@ public:
const std::string hostname() const { return hostname_; }
const std::string path() const { return path_; }
const nlohmann::json::json_pointer pointer() const { return pointer_; }
const json::json_pointer pointer() const { return pointer_; }
const std::string url() const { return location(); }
const std::string location() const;
@ -92,7 +92,7 @@ public:
json_uri append(const std::string &field) const
{
json_uri u = *this;
u.pointer_.push_back(field);
u.pointer_ /= field;
return u;
}
@ -121,7 +121,7 @@ class basic_error_handler
bool error_{false};
public:
virtual void error(const nlohmann::json::json_pointer & /*path*/, const json & /* instance */, const std::string & /*message*/)
virtual void error(const json::json_pointer & /*path*/, const json & /* instance */, const std::string & /*message*/)
{
error_ = true;
}

View File

@ -91,7 +91,7 @@ void json_uri::update(const std::string &uri)
}
}
pointer_ = nlohmann::json::json_pointer(pointer);
pointer_ = json::json_pointer(pointer);
}
const std::string json_uri::location() const

View File

@ -39,7 +39,7 @@ public:
schema(root_schema *root)
: root_(root) {}
virtual void validate(const nlohmann::json::json_pointer &ptr, const json &instance, basic_error_handler &e) const = 0;
virtual void validate(const json::json_pointer &ptr, const json &instance, basic_error_handler &e) const = 0;
static std::shared_ptr<schema> make(json &schema,
root_schema *root,
@ -52,7 +52,7 @@ class schema_ref : public schema
const std::string id_;
std::shared_ptr<schema> target_;
void validate(const nlohmann::json::json_pointer &ptr, const json &instance, basic_error_handler &e) const final
void validate(const json::json_pointer &ptr, const json &instance, basic_error_handler &e) const final
{
if (target_)
target_->validate(ptr, instance, e);
@ -83,8 +83,8 @@ class root_schema : public schema
std::shared_ptr<schema> root_;
struct schema_file {
std::map<nlohmann::json::json_pointer, std::shared_ptr<schema>> schemas;
std::map<nlohmann::json::json_pointer, std::shared_ptr<schema_ref>> unresolved; // contains all unresolved references from any other file seen during parsing
std::map<json::json_pointer, std::shared_ptr<schema>> schemas;
std::map<json::json_pointer, std::shared_ptr<schema_ref>> unresolved; // contains all unresolved references from any other file seen during parsing
json unknown_keywords;
};
@ -211,7 +211,7 @@ public:
} while (1);
}
void validate(const nlohmann::json::json_pointer &ptr, const json &instance, basic_error_handler &e) const final
void validate(const json::json_pointer &ptr, const json &instance, basic_error_handler &e) const final
{
if (root_)
root_->validate(ptr, instance, e);
@ -230,7 +230,7 @@ class logical_not : public schema
{
std::shared_ptr<schema> subschema_;
void validate(const nlohmann::json::json_pointer &ptr, const json &instance, basic_error_handler &e) const final
void validate(const json::json_pointer &ptr, const json &instance, basic_error_handler &e) const final
{
basic_error_handler err;
subschema_->validate(ptr, instance, err);
@ -260,7 +260,7 @@ class logical_combination : public schema
{
std::vector<std::shared_ptr<schema>> subschemata_;
void validate(const nlohmann::json::json_pointer &ptr, const json &instance, basic_error_handler &e) const final
void validate(const json::json_pointer &ptr, const json &instance, basic_error_handler &e) const final
{
size_t count = 0;
@ -268,27 +268,21 @@ class logical_combination : public schema
basic_error_handler err;
s->validate(ptr, instance, err);
if (err) {
//sub_schema_err << " one schema failed because: " << e.what() << "\n";
if (combine_logic == allOf) {
e.error(ptr, instance, "at least one schema has failed, but all of them are required to validate.");
return;
}
} else
if (!err)
count++;
if (combine_logic == oneOf && count > 1) {
e.error(ptr, instance, "more than one schema has succeeded, but exactly one of them is required to validate.");
return;
}
if (combine_logic == anyOf && count == 1)
if (is_validate_complete(instance, ptr, e, err, count))
return;
}
if ((combine_logic == anyOf || combine_logic == oneOf) && count == 0)
if (count == 0)
e.error(ptr, instance, "no validation has succeeded but one of them is required to validate.");
}
// specialized for each of the logical_combination_types
static const std::string key;
static bool is_validate_complete(const json &, const json::json_pointer &, basic_error_handler &, bool, size_t);
public:
logical_combination(json &sch,
root_schema *root,
@ -296,24 +290,43 @@ public:
: schema(root)
{
size_t c = 0;
std::string key;
switch (combine_logic) {
case allOf:
key = "allOf";
break;
case oneOf:
key = "oneOf";
break;
case anyOf:
key = "anyOf";
break;
}
for (auto &subschema : sch)
subschemata_.push_back(schema::make(subschema, root, {key, std::to_string(c++)}, uris));
// value of allOf, anyOf, and oneOf "MUST be a non-empty array"
// TODO error/throw? when subschemata_.empty()
}
};
template <>
const std::string logical_combination<allOf>::key = "allOf";
template <>
const std::string logical_combination<anyOf>::key = "anyOf";
template <>
const std::string logical_combination<oneOf>::key = "oneOf";
template <>
bool logical_combination<allOf>::is_validate_complete(const json &instance, const json::json_pointer &ptr, basic_error_handler &e, bool err, size_t)
{
if (err)
e.error(ptr, instance, "at least one schema has failed, but all of them are required to validate.");
return err;
}
template <>
bool logical_combination<anyOf>::is_validate_complete(const json &, const json::json_pointer &, basic_error_handler &, bool, size_t count)
{
return count == 1;
}
template <>
bool logical_combination<oneOf>::is_validate_complete(const json &instance, const json::json_pointer &ptr, basic_error_handler &e, bool, size_t count)
{
if (count > 1)
e.error(ptr, instance, "more than one schema has succeeded, but exactly one of them is required to validate.");
return count > 1;
}
class type_schema : public schema
{
std::vector<std::shared_ptr<schema>> type_;
@ -328,7 +341,7 @@ class type_schema : public schema
std::shared_ptr<schema> if_, then_, else_;
void validate(const nlohmann::json::json_pointer &ptr, const json &instance, basic_error_handler &e) const override final
void validate(const json::json_pointer &ptr, const json &instance, basic_error_handler &e) const override final
{
// depending on the type of instance run the type specific validator - if present
auto type = type_[(uint8_t) instance.type()];
@ -340,8 +353,8 @@ class type_schema : public schema
if (enum_.first) {
bool seen_in_enum = false;
for (auto &e : enum_.second)
if (instance == e) {
for (auto &v : enum_.second)
if (instance == v) {
seen_in_enum = true;
break;
}
@ -509,7 +522,7 @@ class string : public schema
return len;
}
void validate(const nlohmann::json::json_pointer &ptr, const json &instance, basic_error_handler &e) const override
void validate(const json::json_pointer &ptr, const json &instance, basic_error_handler &e) const override
{
if (minLength_.first) {
if (utf8_length(instance) < minLength_.second) {
@ -591,15 +604,15 @@ class numeric : public schema
std::pair<bool, json::number_float_t> multipleOf_{false, 0};
// multipleOf - if the rest of the division is 0 -> OK
bool violates_multiple_of(json::number_float_t x) const
// multipleOf - if the remainder of the division is 0 -> OK
bool violates_multiple_of(T x) const
{
json::number_integer_t n = static_cast<json::number_integer_t>(x / multipleOf_.second);
double res = (x - n * multipleOf_.second);
return fabs(res) > std::numeric_limits<json::number_float_t>::epsilon();
double res = std::remainder(x, multipleOf_.second);
double eps = std::nextafter(x, 0) - x;
return std::fabs(res) > std::fabs(eps);
}
void validate(const nlohmann::json::json_pointer &ptr, const json &instance, basic_error_handler &e) const override
void validate(const json::json_pointer &ptr, const json &instance, basic_error_handler &e) const override
{
T value = instance; // conversion of json to value_type
@ -658,7 +671,7 @@ public:
class null : public schema
{
void validate(const nlohmann::json::json_pointer &ptr, const json &instance, basic_error_handler &e) const override
void validate(const json::json_pointer &ptr, const json &instance, basic_error_handler &e) const override
{
if (!instance.is_null())
e.error(ptr, instance, "expected to be null");
@ -671,7 +684,7 @@ public:
class boolean_type : public schema
{
void validate(const nlohmann::json::json_pointer &, const json &, basic_error_handler &) const override {}
void validate(const json::json_pointer &, const json &, basic_error_handler &) const override {}
public:
boolean_type(json &, root_schema *root)
@ -681,7 +694,7 @@ public:
class boolean : public schema
{
bool true_;
void validate(const nlohmann::json::json_pointer &ptr, const json &instance, basic_error_handler &e) const override
void validate(const json::json_pointer &ptr, const json &instance, basic_error_handler &e) const override
{
if (!true_) { // false schema
// empty array
@ -705,7 +718,7 @@ class required : public schema
{
const std::vector<std::string> required_;
void validate(const nlohmann::json::json_pointer &ptr, const json &instance, basic_error_handler &e) const override final
void validate(const json::json_pointer &ptr, const json &instance, basic_error_handler &e) const override final
{
for (auto &r : required_)
if (instance.find(r) == instance.end())
@ -733,7 +746,7 @@ class object : public schema
std::shared_ptr<schema> propertyNames_;
void validate(const nlohmann::json::json_pointer &ptr, const json &instance, basic_error_handler &e) const override
void validate(const json::json_pointer &ptr, const json &instance, basic_error_handler &e) const override
{
if (maxProperties_.first && instance.size() > maxProperties_.second)
e.error(ptr, instance, "too many properties.");
@ -771,7 +784,7 @@ class object : public schema
for (auto &dep : dependencies_) {
auto prop = instance.find(dep.first);
if (prop != instance.end()) // if dependency-property is present in instance
if (prop != instance.end()) // if dependency-property is present in instance
dep.second->validate(ptr / dep.first, instance, e); // validate
}
}
@ -867,7 +880,7 @@ class array : public schema
std::shared_ptr<schema> contains_;
void validate(const nlohmann::json::json_pointer &ptr, const json &instance, basic_error_handler &e) const override
void validate(const json::json_pointer &ptr, const json &instance, basic_error_handler &e) const override
{
if (maxItems_.first && instance.size() > maxItems_.second)
e.error(ptr, instance, "has too many items.");
@ -1109,7 +1122,7 @@ void json_validator::validate(const json &instance)
void json_validator::validate(const json &instance, basic_error_handler &err)
{
nlohmann::json::json_pointer ptr;
json::json_pointer ptr;
root_->validate(ptr, instance, err);
}

View File

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

View File

@ -0,0 +1 @@
1.2

View File

@ -0,0 +1,3 @@
{
"multipleOf": 0.1
}