Merge branch 'master' into fix/cmake_install_prefix
This commit is contained in:
commit
81c660a1c3
@ -13,6 +13,7 @@ add_library(nlohmann_json_schema_validator
|
||||
src/json-schema-draft7.json.cpp
|
||||
src/json-uri.cpp
|
||||
src/json-validator.cpp
|
||||
src/json-patch.cpp
|
||||
src/string-format-check.cpp)
|
||||
|
||||
target_include_directories(nlohmann_json_schema_validator
|
||||
@ -29,6 +30,10 @@ set_target_properties(nlohmann_json_schema_validator
|
||||
VERSION ${PROJECT_VERSION}
|
||||
SOVERSION 1)
|
||||
|
||||
# if used as a sub-directory, do not create install-rules -
|
||||
# because of the dependency to nlohmann_json.
|
||||
set(JSON_VALIDATOR_INSTALL ON)
|
||||
|
||||
# here we decice how nlohmann::json is found and used to build this project
|
||||
|
||||
# first, check whether a nlohmann_json::nlohmann_json target exists already
|
||||
@ -39,11 +44,15 @@ if(TARGET nlohmann_json::nlohmann_json)
|
||||
nlohmann_json_schema_validator
|
||||
PUBLIC nlohmann_json::nlohmann_json)
|
||||
|
||||
set(JSON_VALIDATOR_INSTALL OFF)
|
||||
|
||||
elseif(TARGET nlohmann_json) # or nlohmann_json, we are used a sub-project next to nlohmann-json's git repo
|
||||
message(STATUS "Found nlohmann_json-target - linking with it")
|
||||
target_link_libraries(
|
||||
nlohmann_json_schema_validator
|
||||
PUBLIC nlohmann_json)
|
||||
set(JSON_VALIDATOR_INSTALL OFF)
|
||||
|
||||
else()
|
||||
if (NOT IS_ABSOLUTE ${nlohmann_json_DIR}) # make nlohmann_json_DIR absolute
|
||||
get_filename_component(nlohmann_json_DIR
|
||||
@ -122,6 +131,7 @@ install(TARGETS nlohmann_json_schema_validator
|
||||
install(FILES src/nlohmann/json-schema.hpp
|
||||
DESTINATION ${CMAKE_INSTALL_PREFIX}/include/nlohmann)
|
||||
|
||||
|
||||
if (BUILD_EXAMPLES)
|
||||
# simple nlohmann_json_schema_validator-executable
|
||||
add_executable(json-schema-validate app/json-schema-validate.cpp)
|
||||
@ -129,6 +139,9 @@ if (BUILD_EXAMPLES)
|
||||
|
||||
add_executable(readme-json-schema app/readme.cpp)
|
||||
target_link_libraries(readme-json-schema nlohmann_json_schema_validator)
|
||||
|
||||
install(TARGETS json-schema-validate readme-json-schema
|
||||
DESTINATION bin)
|
||||
endif()
|
||||
|
||||
if (BUILD_TESTS)
|
||||
@ -136,35 +149,36 @@ if (BUILD_TESTS)
|
||||
enable_testing()
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
#---------------------------------------------------------------------------##
|
||||
# Set Up the Project Targets and Config Files for CMake
|
||||
#---------------------------------------------------------------------------##
|
||||
|
||||
# Set the install path to the cmake config files
|
||||
set(INSTALL_CMAKE_DIR ${CMAKE_INSTALL_PREFIX}/lib/cmake/${PROJECT_NAME})
|
||||
if(JSON_VALIDATOR_INSTALL)
|
||||
# Set Up the Project Targets and Config Files for CMake
|
||||
|
||||
# Create the ConfigVersion file
|
||||
include(CMakePackageConfigHelpers) # write_basic_package_version_file
|
||||
write_basic_package_version_file( ${PROJECT_NAME}ConfigVersion.cmake
|
||||
VERSION ${PACKAGE_VERSION}
|
||||
COMPATIBILITY SameMajorVersion)
|
||||
# Set the install path to the cmake config files
|
||||
set(INSTALL_CMAKE_DIR ${CMAKE_INSTALL_PREFIX}/lib/cmake/${PROJECT_NAME})
|
||||
|
||||
# Get the relative path from the INSTALL_CMAKE_DIR to the include directory
|
||||
file(RELATIVE_PATH REL_INCLUDE_DIR "${INSTALL_CMAKE_DIR}" "${CMAKE_INSTALL_PREFIX}/include")
|
||||
# Create the ConfigVersion file
|
||||
include(CMakePackageConfigHelpers) # write_basic_package_version_file
|
||||
write_basic_package_version_file( ${PROJECT_NAME}ConfigVersion.cmake
|
||||
VERSION ${PACKAGE_VERSION}
|
||||
COMPATIBILITY SameMajorVersion)
|
||||
|
||||
# Get the relative path from the INSTALL_CMAKE_DIR to the include directory
|
||||
file(RELATIVE_PATH REL_INCLUDE_DIR "${INSTALL_CMAKE_DIR}" "${CMAKE_INSTALL_PREFIX}/include")
|
||||
|
||||
|
||||
# Configure the Config.cmake file with the proper include directory
|
||||
set(CONF_INCLUDE_DIRS "\${JSON_SCHEMA_VALIDATOR_CMAKE_DIR}/${REL_INCLUDE_DIR}")
|
||||
configure_file(${PROJECT_NAME}Config.cmake.in
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" @ONLY)
|
||||
# Configure the Config.cmake file with the proper include directory
|
||||
set(CONF_INCLUDE_DIRS "\${JSON_SCHEMA_VALIDATOR_CMAKE_DIR}/${REL_INCLUDE_DIR}")
|
||||
configure_file(${PROJECT_NAME}Config.cmake.in
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" @ONLY)
|
||||
|
||||
# Install the Config.cmake and ConfigVersion.cmake files
|
||||
install(FILES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
|
||||
DESTINATION "${INSTALL_CMAKE_DIR}")
|
||||
# Install the Config.cmake and ConfigVersion.cmake files
|
||||
install(FILES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
|
||||
DESTINATION "${INSTALL_CMAKE_DIR}")
|
||||
|
||||
# Install Targets
|
||||
install(EXPORT ${PROJECT_NAME}Targets
|
||||
FILE ${PROJECT_NAME}Targets.cmake
|
||||
DESTINATION "${INSTALL_CMAKE_DIR}")
|
||||
# Install Targets
|
||||
install(EXPORT ${PROJECT_NAME}Targets
|
||||
FILE ${PROJECT_NAME}Targets.cmake
|
||||
DESTINATION "${INSTALL_CMAKE_DIR}")
|
||||
endif()
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
|
||||
#include <nlohmann/json-schema.hpp>
|
||||
|
||||
@ -12,16 +12,25 @@ static json person_schema = R"(
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "A person",
|
||||
"properties": {
|
||||
"name": {
|
||||
"description": "Name",
|
||||
"type": "string"
|
||||
},
|
||||
"age": {
|
||||
"description": "Age of the person",
|
||||
"type": "number",
|
||||
"minimum": 2,
|
||||
"maximum": 200
|
||||
"name": {
|
||||
"description": "Name",
|
||||
"type": "string"
|
||||
},
|
||||
"age": {
|
||||
"description": "Age of the person",
|
||||
"type": "number",
|
||||
"minimum": 2,
|
||||
"maximum": 200
|
||||
},
|
||||
"address":{
|
||||
"type": "object",
|
||||
"properties":{
|
||||
"street":{
|
||||
"type": "string",
|
||||
"default": "Abbey Road"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
@ -34,7 +43,8 @@ static json person_schema = R"(
|
||||
|
||||
// The people are defined with brace initialization
|
||||
static json bad_person = {{"age", 42}};
|
||||
static json good_person = {{"name", "Albert"}, {"age", 42}};
|
||||
static json good_person = {{"name", "Albert"}, {"age", 42}, {"address", {{"street", "Main Street"}}}};
|
||||
static json good_defaulted_person = {{"name", "Knut"}, {"age", 69}, {"address", {}}};
|
||||
|
||||
int main()
|
||||
{
|
||||
@ -51,12 +61,13 @@ int main()
|
||||
|
||||
/* json-parse the people - API of 1.0.0, default throwing error handler */
|
||||
|
||||
for (auto &person : {bad_person, good_person}) {
|
||||
for (auto &person : {bad_person, good_person, good_defaulted_person}) {
|
||||
std::cout << "About to validate this person:\n"
|
||||
<< std::setw(2) << person << std::endl;
|
||||
try {
|
||||
validator.validate(person); // validate the document - uses the default throwing error-handler
|
||||
auto defaultPatch = validator.validate(person); // validate the document - uses the default throwing error-handler
|
||||
std::cout << "Validation succeeded\n";
|
||||
std::cout << "Patch with defaults: " << defaultPatch.dump(2) << std::endl;
|
||||
} catch (const std::exception &e) {
|
||||
std::cerr << "Validation failed, here is why: " << e.what() << "\n";
|
||||
}
|
||||
@ -72,7 +83,6 @@ int main()
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
for (auto &person : {bad_person, good_person}) {
|
||||
std::cout << "About to validate this person:\n"
|
||||
<< std::setw(2) << person << std::endl;
|
||||
|
||||
69
conanfile.py
Normal file
69
conanfile.py
Normal file
@ -0,0 +1,69 @@
|
||||
import os
|
||||
import re
|
||||
from conans import load, tools, ConanFile, CMake
|
||||
|
||||
|
||||
def get_version():
|
||||
try:
|
||||
version = os.getenv('PROJECT_VERSION', None)
|
||||
if version:
|
||||
return version
|
||||
|
||||
content = load('CMakeLists.txt')
|
||||
version = re.search('set\(PROJECT_VERSION (.*)\)', content).group(1)
|
||||
return version.strip()
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
class JsonSchemaValidatorConan(ConanFile):
|
||||
name = 'JsonSchemaValidator'
|
||||
version = get_version()
|
||||
url = 'https://github.com/pboettch/json-schema-validator'
|
||||
license = 'MIT'
|
||||
settings = 'os', 'compiler', 'build_type', 'arch'
|
||||
options = {
|
||||
'shared': [True, False],
|
||||
'fPIC': [True, False]
|
||||
}
|
||||
default_options = {
|
||||
'shared': False,
|
||||
'fPIC': True
|
||||
}
|
||||
generators = "cmake"
|
||||
exports_sources = [
|
||||
'CMakeLists.txt',
|
||||
'nlohmann_json_schema_validatorConfig.cmake.in',
|
||||
'src/*',
|
||||
'app/*',
|
||||
]
|
||||
|
||||
requires = (
|
||||
'nlohmann_json/3.7.3'
|
||||
)
|
||||
|
||||
def build(self):
|
||||
cmake = CMake(self)
|
||||
cmake.definitions['nlohmann_json_DIR'] = os.path.join(self.deps_cpp_info['nlohmann_json'].rootpath, 'include')
|
||||
cmake.definitions['BUILD_EXAMPLES'] = True
|
||||
cmake.definitions['BUILD_TESTS'] = False
|
||||
cmake.configure()
|
||||
cmake.build()
|
||||
|
||||
def package(self):
|
||||
cmake = CMake(self)
|
||||
cmake.install()
|
||||
|
||||
def package_info(self):
|
||||
includedir = os.path.join(self.package_folder, "include")
|
||||
self.cpp_info.includedirs = [includedir]
|
||||
|
||||
libdir = os.path.join(self.package_folder, "lib")
|
||||
self.cpp_info.libdirs = [libdir]
|
||||
self.cpp_info.libs += tools.collect_libs(self, libdir)
|
||||
|
||||
bindir = os.path.join(self.package_folder, "bin")
|
||||
self.output.info("Appending PATH environment variable: {}".format(bindir))
|
||||
self.env_info.PATH.append(bindir)
|
||||
|
||||
self.user_info.VERSION = self.version
|
||||
75
src/json-patch.cpp
Normal file
75
src/json-patch.cpp
Normal file
@ -0,0 +1,75 @@
|
||||
#include "json-patch.hpp"
|
||||
|
||||
namespace nlohmann
|
||||
{
|
||||
|
||||
json_patch::json_patch(json &&patch)
|
||||
: j_{std::move(patch)}
|
||||
{
|
||||
validateJsonPatch(j_);
|
||||
}
|
||||
|
||||
json_patch::json_patch(const json &patch)
|
||||
: j_{std::move(patch)}
|
||||
{
|
||||
validateJsonPatch(j_);
|
||||
}
|
||||
|
||||
json_patch &json_patch::add(std::string path, json value)
|
||||
{
|
||||
j_.push_back(json{{"op", "add"}, {"path", std::move(path)}, {"value", std::move(value)}});
|
||||
return *this;
|
||||
}
|
||||
|
||||
json_patch &json_patch::replace(std::string path, json value)
|
||||
{
|
||||
j_.push_back(json{{"op", "replace"}, {"path", std::move(path)}, {"value", std::move(value)}});
|
||||
return *this;
|
||||
}
|
||||
|
||||
json_patch &json_patch::remove(std::string path)
|
||||
{
|
||||
j_.push_back(json{{"op", "remove"}, {"path", std::move(path)}});
|
||||
return *this;
|
||||
}
|
||||
|
||||
void json_patch::validateJsonPatch(json const &patch)
|
||||
{
|
||||
if (!patch.is_array()) {
|
||||
throw JsonPatchFormatException{"Json is not an array"};
|
||||
}
|
||||
|
||||
for (auto const &op : patch) {
|
||||
if (!op.is_object()) {
|
||||
throw JsonPatchFormatException{"Each json patch entry needs to be an op object"};
|
||||
}
|
||||
|
||||
if (!op.contains("op")) {
|
||||
throw JsonPatchFormatException{"Each json patch entry needs op element"};
|
||||
}
|
||||
|
||||
const auto opType = op["op"].get<std::string>();
|
||||
if ((opType != "add") && (opType != "remove") && (opType != "replace")) {
|
||||
throw JsonPatchFormatException{std::string{"Operation "} + opType + std::string{"is invalid"}};
|
||||
}
|
||||
|
||||
if (!op.contains("path")) {
|
||||
throw JsonPatchFormatException{"Each json patch entry needs path element"};
|
||||
}
|
||||
|
||||
try {
|
||||
// try parse to path
|
||||
[[maybe_unused]] const auto p = json::json_pointer{op["path"].get<std::string>()};
|
||||
} catch (json::exception &e) {
|
||||
throw JsonPatchFormatException{e.what()};
|
||||
}
|
||||
|
||||
if (opType != "remove") {
|
||||
if (!op.contains("value")) {
|
||||
throw JsonPatchFormatException{"Remove and replace needs value element"};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace nlohmann
|
||||
38
src/json-patch.hpp
Normal file
38
src/json-patch.hpp
Normal file
@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <string>
|
||||
|
||||
namespace nlohmann
|
||||
{
|
||||
class JsonPatchFormatException : public std::exception
|
||||
{
|
||||
public:
|
||||
explicit JsonPatchFormatException(std::string msg)
|
||||
: ex_{std::move(msg)} {}
|
||||
|
||||
inline const char *what() const noexcept override final { return ex_.c_str(); }
|
||||
|
||||
private:
|
||||
std::string ex_;
|
||||
};
|
||||
|
||||
class json_patch
|
||||
{
|
||||
public:
|
||||
json_patch() = default;
|
||||
json_patch(json &&patch);
|
||||
json_patch(const json &patch);
|
||||
|
||||
json_patch &add(std::string path, json value);
|
||||
json_patch &replace(std::string path, json value);
|
||||
json_patch &remove(std::string path);
|
||||
|
||||
operator json() const { return j_; }
|
||||
|
||||
private:
|
||||
json j_;
|
||||
|
||||
static void validateJsonPatch(json const &patch);
|
||||
};
|
||||
} // namespace nlohmann
|
||||
@ -44,7 +44,7 @@ void json_uri::update(const std::string &uri)
|
||||
|
||||
auto location = uri.substr(0, pointer_separator);
|
||||
|
||||
if (location.size()) { // a location part has been found
|
||||
if (location.size()) { // a location part has been found
|
||||
|
||||
// if it is an URN take it as it is
|
||||
if (location.find("urn:") == 0) {
|
||||
@ -99,7 +99,7 @@ void json_uri::update(const std::string &uri)
|
||||
identifier_ = pointer;
|
||||
}
|
||||
|
||||
const std::string json_uri::location() const
|
||||
std::string json_uri::location() const
|
||||
{
|
||||
if (urn_.size())
|
||||
return urn_;
|
||||
|
||||
@ -8,11 +8,14 @@
|
||||
*/
|
||||
#include <nlohmann/json-schema.hpp>
|
||||
|
||||
#include "json-patch.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
|
||||
using nlohmann::json;
|
||||
using nlohmann::json_patch;
|
||||
using nlohmann::json_uri;
|
||||
using nlohmann::json_schema::root_schema;
|
||||
using namespace nlohmann::json_schema;
|
||||
@ -30,16 +33,25 @@ using namespace nlohmann::json_schema;
|
||||
namespace
|
||||
{
|
||||
|
||||
static const json EmptyDefault{};
|
||||
|
||||
class schema
|
||||
{
|
||||
protected:
|
||||
root_schema *root_;
|
||||
|
||||
public:
|
||||
virtual ~schema() = default;
|
||||
|
||||
schema(root_schema *root)
|
||||
: root_(root) {}
|
||||
|
||||
virtual void validate(const json::json_pointer &ptr, const json &instance, error_handler &e) const = 0;
|
||||
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
|
||||
{
|
||||
return EmptyDefault;
|
||||
}
|
||||
|
||||
static std::shared_ptr<schema> make(json &schema,
|
||||
root_schema *root,
|
||||
@ -52,14 +64,24 @@ class schema_ref : public schema
|
||||
const std::string id_;
|
||||
std::shared_ptr<schema> target_;
|
||||
|
||||
void validate(const json::json_pointer &ptr, const json &instance, error_handler &e) const final
|
||||
void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const final
|
||||
{
|
||||
if (target_)
|
||||
target_->validate(ptr, instance, e);
|
||||
target_->validate(ptr, instance, patch, e);
|
||||
else
|
||||
e.error(ptr, instance, "unresolved schema-reference " + id_);
|
||||
}
|
||||
|
||||
const json &defaultValue(const json::json_pointer &ptr, const json &instance, error_handler &e) const override
|
||||
{
|
||||
if (target_)
|
||||
target_->defaultValue(ptr, instance, e);
|
||||
else
|
||||
e.error(ptr, instance, "unresolved schema-reference " + id_);
|
||||
|
||||
return EmptyDefault;
|
||||
}
|
||||
|
||||
public:
|
||||
schema_ref(const std::string &id, root_schema *root)
|
||||
: schema(root), id_(id) {}
|
||||
@ -101,9 +123,9 @@ class root_schema : public schema
|
||||
}
|
||||
|
||||
public:
|
||||
root_schema(schema_loader loader,
|
||||
format_checker format)
|
||||
: schema(this), loader_(loader), format_check_(format) {}
|
||||
root_schema(schema_loader &&loader,
|
||||
format_checker &&format)
|
||||
: schema(this), loader_(std::move(loader)), format_check_(std::move(format)) {}
|
||||
|
||||
format_checker &format_check() { return format_check_; }
|
||||
|
||||
@ -208,13 +230,23 @@ public:
|
||||
} while (1);
|
||||
}
|
||||
|
||||
void validate(const json::json_pointer &ptr, const json &instance, error_handler &e) const final
|
||||
void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const final
|
||||
{
|
||||
if (root_)
|
||||
root_->validate(ptr, instance, e);
|
||||
root_->validate(ptr, instance, patch, e);
|
||||
else
|
||||
e.error(ptr, "", "no root schema has yet been set for validating an instance");
|
||||
}
|
||||
|
||||
const json &defaultValue(const json::json_pointer &ptr, const json &instance, error_handler &e) const override
|
||||
{
|
||||
if (root_)
|
||||
root_->defaultValue(ptr, instance, e);
|
||||
else
|
||||
e.error(ptr, "", "no root schema has yet been set for validating an instance");
|
||||
|
||||
return EmptyDefault;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace json_schema
|
||||
@ -248,15 +280,20 @@ class logical_not : public schema
|
||||
{
|
||||
std::shared_ptr<schema> subschema_;
|
||||
|
||||
void validate(const json::json_pointer &ptr, const json &instance, error_handler &e) const final
|
||||
void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const final
|
||||
{
|
||||
first_error_handler esub;
|
||||
subschema_->validate(ptr, instance, esub);
|
||||
subschema_->validate(ptr, instance, patch, esub);
|
||||
|
||||
if (!esub)
|
||||
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
|
||||
{
|
||||
return subschema_->defaultValue(ptr, instance, e);
|
||||
}
|
||||
|
||||
public:
|
||||
logical_not(json &sch,
|
||||
root_schema *root,
|
||||
@ -278,13 +315,13 @@ class logical_combination : public schema
|
||||
{
|
||||
std::vector<std::shared_ptr<schema>> subschemata_;
|
||||
|
||||
void validate(const json::json_pointer &ptr, const json &instance, error_handler &e) const final
|
||||
void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const final
|
||||
{
|
||||
size_t count = 0;
|
||||
|
||||
for (auto &s : subschemata_) {
|
||||
first_error_handler esub;
|
||||
s->validate(ptr, instance, esub);
|
||||
s->validate(ptr, instance, patch, esub);
|
||||
if (!esub)
|
||||
count++;
|
||||
|
||||
@ -348,6 +385,7 @@ bool logical_combination<oneOf>::is_validate_complete(const json &instance, cons
|
||||
|
||||
class type_schema : public schema
|
||||
{
|
||||
json defaultValue_{};
|
||||
std::vector<std::shared_ptr<schema>> type_;
|
||||
std::pair<bool, json> enum_, const_;
|
||||
std::vector<std::shared_ptr<schema>> logic_;
|
||||
@ -360,13 +398,18 @@ class type_schema : public schema
|
||||
|
||||
std::shared_ptr<schema> if_, then_, else_;
|
||||
|
||||
void validate(const json::json_pointer &ptr, const json &instance, error_handler &e) const override final
|
||||
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
|
||||
auto type = type_[(uint8_t) instance.type()];
|
||||
|
||||
if (type)
|
||||
type->validate(ptr, instance, e);
|
||||
type->validate(ptr, instance, patch, e);
|
||||
else
|
||||
e.error(ptr, instance, "unexpected instance type");
|
||||
|
||||
@ -387,18 +430,18 @@ class type_schema : public schema
|
||||
e.error(ptr, instance, "instance not const");
|
||||
|
||||
for (auto l : logic_)
|
||||
l->validate(ptr, instance, e);
|
||||
l->validate(ptr, instance, patch, e);
|
||||
|
||||
if (if_) {
|
||||
first_error_handler err;
|
||||
|
||||
if_->validate(ptr, instance, err);
|
||||
if_->validate(ptr, instance, patch, err);
|
||||
if (!err) {
|
||||
if (then_)
|
||||
then_->validate(ptr, instance, e);
|
||||
then_->validate(ptr, instance, patch, e);
|
||||
} else {
|
||||
if (else_)
|
||||
else_->validate(ptr, instance, e);
|
||||
else_->validate(ptr, instance, patch, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -450,6 +493,11 @@ public:
|
||||
sch.erase(attr);
|
||||
}
|
||||
|
||||
const auto defaultAttr = sch.find("default");
|
||||
if (defaultAttr != sch.end()) {
|
||||
defaultValue_ = defaultAttr.value();
|
||||
}
|
||||
|
||||
for (auto &key : known_keywords)
|
||||
sch.erase(key);
|
||||
|
||||
@ -542,7 +590,7 @@ class string : public schema
|
||||
return len;
|
||||
}
|
||||
|
||||
void validate(const json::json_pointer &ptr, const json &instance, error_handler &e) const override
|
||||
void validate(const json::json_pointer &ptr, const json &instance, json_patch &, error_handler &e) const override
|
||||
{
|
||||
if (minLength_.first) {
|
||||
if (utf8_length(instance) < minLength_.second) {
|
||||
@ -632,7 +680,7 @@ class numeric : public schema
|
||||
return std::fabs(res) > std::fabs(eps);
|
||||
}
|
||||
|
||||
void validate(const json::json_pointer &ptr, const json &instance, error_handler &e) const override
|
||||
void validate(const json::json_pointer &ptr, const json &instance, json_patch &, error_handler &e) const override
|
||||
{
|
||||
T value = instance; // conversion of json to value_type
|
||||
|
||||
@ -691,7 +739,7 @@ public:
|
||||
|
||||
class null : public schema
|
||||
{
|
||||
void validate(const json::json_pointer &ptr, const json &instance, error_handler &e) const override
|
||||
void validate(const json::json_pointer &ptr, const json &instance, json_patch &, error_handler &e) const override
|
||||
{
|
||||
if (!instance.is_null())
|
||||
e.error(ptr, instance, "expected to be null");
|
||||
@ -704,7 +752,7 @@ public:
|
||||
|
||||
class boolean_type : public schema
|
||||
{
|
||||
void validate(const json::json_pointer &, const json &, error_handler &) const override {}
|
||||
void validate(const json::json_pointer &, const json &, json_patch &, error_handler &) const override {}
|
||||
|
||||
public:
|
||||
boolean_type(json &, root_schema *root)
|
||||
@ -714,7 +762,7 @@ public:
|
||||
class boolean : public schema
|
||||
{
|
||||
bool true_;
|
||||
void validate(const json::json_pointer &ptr, const json &instance, error_handler &e) const override
|
||||
void validate(const json::json_pointer &ptr, const json &instance, json_patch &, error_handler &e) const override
|
||||
{
|
||||
if (!true_) { // false schema
|
||||
// empty array
|
||||
@ -738,7 +786,7 @@ class required : public schema
|
||||
{
|
||||
const std::vector<std::string> required_;
|
||||
|
||||
void validate(const json::json_pointer &ptr, const json &instance, error_handler &e) const override final
|
||||
void validate(const json::json_pointer &ptr, const json &instance, json_patch &, error_handler &e) const override final
|
||||
{
|
||||
for (auto &r : required_)
|
||||
if (instance.find(r) == instance.end())
|
||||
@ -766,7 +814,7 @@ class object : public schema
|
||||
|
||||
std::shared_ptr<schema> propertyNames_;
|
||||
|
||||
void validate(const json::json_pointer &ptr, const json &instance, error_handler &e) const override
|
||||
void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const override
|
||||
{
|
||||
if (maxProperties_.first && instance.size() > maxProperties_.second)
|
||||
e.error(ptr, instance, "too many properties");
|
||||
@ -781,14 +829,14 @@ class object : public schema
|
||||
// for each property in instance
|
||||
for (auto &p : instance.items()) {
|
||||
if (propertyNames_)
|
||||
propertyNames_->validate(ptr, p.key(), e);
|
||||
propertyNames_->validate(ptr, p.key(), patch, e);
|
||||
|
||||
bool a_prop_or_pattern_matched = false;
|
||||
auto schema_p = properties_.find(p.key());
|
||||
// check if it is in "properties"
|
||||
if (schema_p != properties_.end()) {
|
||||
a_prop_or_pattern_matched = true;
|
||||
schema_p->second->validate(ptr / p.key(), p.value(), e);
|
||||
schema_p->second->validate(ptr / p.key(), p.value(), patch, e);
|
||||
}
|
||||
|
||||
#ifndef NO_STD_REGEX
|
||||
@ -796,23 +844,34 @@ class object : public schema
|
||||
for (auto &schema_pp : patternProperties_)
|
||||
if (REGEX_NAMESPACE::regex_search(p.key(), schema_pp.first)) {
|
||||
a_prop_or_pattern_matched = true;
|
||||
schema_pp.second->validate(ptr / p.key(), p.value(), e);
|
||||
schema_pp.second->validate(ptr / p.key(), p.value(), patch, e);
|
||||
}
|
||||
#endif
|
||||
|
||||
// check additionalProperties as a last resort
|
||||
if (!a_prop_or_pattern_matched && additionalProperties_) {
|
||||
first_error_handler additional_prop_err;
|
||||
additionalProperties_->validate(ptr / p.key(), p.value(), additional_prop_err);
|
||||
additionalProperties_->validate(ptr / p.key(), p.value(), patch, additional_prop_err);
|
||||
if (additional_prop_err)
|
||||
e.error(ptr, instance, "validation failed for additional property '" + p.key() + "': " + additional_prop_err.message_);
|
||||
}
|
||||
}
|
||||
|
||||
// reverse search
|
||||
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.empty()) { // if default value is available
|
||||
patch.add((ptr / prop.first), defaultValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &dep : dependencies_) {
|
||||
auto prop = instance.find(dep.first);
|
||||
if (prop != instance.end()) // if dependency-property is present in instance
|
||||
dep.second->validate(ptr / dep.first, instance, e); // validate
|
||||
if (prop != instance.end()) // if dependency-property is present in instance
|
||||
dep.second->validate(ptr / dep.first, instance, patch, e); // validate
|
||||
}
|
||||
}
|
||||
|
||||
@ -907,7 +966,7 @@ class array : public schema
|
||||
|
||||
std::shared_ptr<schema> contains_;
|
||||
|
||||
void validate(const json::json_pointer &ptr, const json &instance, error_handler &e) const override
|
||||
void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const override
|
||||
{
|
||||
if (maxItems_.first && instance.size() > maxItems_.second)
|
||||
e.error(ptr, instance, "array has too many items");
|
||||
@ -926,7 +985,7 @@ class array : public schema
|
||||
size_t index = 0;
|
||||
if (items_schema_)
|
||||
for (auto &i : instance) {
|
||||
items_schema_->validate(ptr / index, i, e);
|
||||
items_schema_->validate(ptr / index, i, patch, e);
|
||||
index++;
|
||||
}
|
||||
else {
|
||||
@ -943,7 +1002,7 @@ class array : public schema
|
||||
if (!item_validator)
|
||||
break;
|
||||
|
||||
item_validator->validate(ptr / index, i, e);
|
||||
item_validator->validate(ptr / index, i, patch, e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -951,7 +1010,7 @@ class array : public schema
|
||||
bool contained = false;
|
||||
for (auto &item : instance) {
|
||||
first_error_handler local_e;
|
||||
contains_->validate(ptr, item, local_e);
|
||||
contains_->validate(ptr, item, patch, local_e);
|
||||
if (!local_e) {
|
||||
contained = true;
|
||||
break;
|
||||
@ -1127,16 +1186,22 @@ namespace json_schema
|
||||
|
||||
json_validator::json_validator(schema_loader loader,
|
||||
format_checker format)
|
||||
: root_(std::unique_ptr<root_schema>(new root_schema(loader, format)))
|
||||
: root_(std::unique_ptr<root_schema>(new root_schema(std::move(loader), std::move(format))))
|
||||
{
|
||||
}
|
||||
|
||||
json_validator::json_validator(const json &schema, schema_loader loader, format_checker format)
|
||||
: json_validator(loader, format)
|
||||
: json_validator(std::move(loader), std::move(format))
|
||||
{
|
||||
set_root_schema(schema);
|
||||
}
|
||||
|
||||
json_validator::json_validator(json &&schema, schema_loader loader, format_checker format)
|
||||
: json_validator(std::move(loader), std::move(format))
|
||||
{
|
||||
set_root_schema(std::move(schema));
|
||||
}
|
||||
|
||||
// move constructor, destructor and move assignment operator can be defaulted here
|
||||
// where root_schema is a complete type
|
||||
json_validator::json_validator(json_validator &&) = default;
|
||||
@ -1148,16 +1213,23 @@ void json_validator::set_root_schema(const json &schema)
|
||||
root_->set_root_schema(schema);
|
||||
}
|
||||
|
||||
void json_validator::validate(const json &instance) const
|
||||
void json_validator::set_root_schema(json &&schema)
|
||||
{
|
||||
throwing_error_handler err;
|
||||
validate(instance, err);
|
||||
root_->set_root_schema(std::move(schema));
|
||||
}
|
||||
|
||||
void json_validator::validate(const json &instance, error_handler &err) const
|
||||
json json_validator::validate(const json &instance) const
|
||||
{
|
||||
throwing_error_handler err;
|
||||
return validate(instance, err);
|
||||
}
|
||||
|
||||
json json_validator::validate(const json &instance, error_handler &err) const
|
||||
{
|
||||
json::json_pointer ptr;
|
||||
root_->validate(ptr, instance, err);
|
||||
json_patch patch{};
|
||||
root_->validate(ptr, instance, patch, err);
|
||||
return patch;
|
||||
}
|
||||
|
||||
} // namespace json_schema
|
||||
|
||||
@ -71,14 +71,14 @@ public:
|
||||
update(uri);
|
||||
}
|
||||
|
||||
const std::string scheme() const { return scheme_; }
|
||||
const std::string authority() const { return authority_; }
|
||||
const std::string path() const { return path_; }
|
||||
const std::string &scheme() const { return scheme_; }
|
||||
const std::string &authority() const { return authority_; }
|
||||
const std::string &path() const { return path_; }
|
||||
|
||||
const json::json_pointer pointer() const { return pointer_; }
|
||||
const std::string identifier() const { return identifier_; }
|
||||
const json::json_pointer &pointer() const { return pointer_; }
|
||||
const std::string &identifier() const { return identifier_; }
|
||||
|
||||
const std::string fragment() const
|
||||
std::string fragment() const
|
||||
{
|
||||
if (identifier_ == "")
|
||||
return pointer_;
|
||||
@ -86,8 +86,8 @@ public:
|
||||
return identifier_;
|
||||
}
|
||||
|
||||
const std::string url() const { return location(); }
|
||||
const std::string location() const;
|
||||
std::string url() const { return location(); }
|
||||
std::string location() const;
|
||||
|
||||
static std::string escape(const std::string &);
|
||||
|
||||
@ -169,19 +169,27 @@ class JSON_SCHEMA_VALIDATOR_API json_validator
|
||||
|
||||
public:
|
||||
json_validator(schema_loader = nullptr, format_checker = nullptr);
|
||||
json_validator(json_validator &&);
|
||||
|
||||
json_validator(const json &, schema_loader = nullptr, format_checker = nullptr);
|
||||
~json_validator();
|
||||
json_validator(json &&, schema_loader = nullptr, format_checker = nullptr);
|
||||
|
||||
json_validator(json_validator &&);
|
||||
json_validator &operator=(json_validator &&);
|
||||
|
||||
json_validator(json_validator const &) = delete;
|
||||
json_validator &operator=(json_validator const &) = delete;
|
||||
|
||||
~json_validator();
|
||||
|
||||
// insert and set the root-schema
|
||||
void set_root_schema(const json &);
|
||||
void set_root_schema(json &&);
|
||||
|
||||
// validate a json-document based on the root-schema
|
||||
void validate(const json &) const;
|
||||
json validate(const json &) const;
|
||||
|
||||
// validate a json-document based on the root-schema with a custom error-handler
|
||||
void validate(const json &, error_handler &) const;
|
||||
json validate(const json &, error_handler &) const;
|
||||
};
|
||||
|
||||
} // namespace json_schema
|
||||
|
||||
@ -35,6 +35,10 @@ add_executable(issue-70-root-schema-constructor issue-70-root-schema-constructor
|
||||
target_link_libraries(issue-70-root-schema-constructor nlohmann_json_schema_validator)
|
||||
add_test(NAME issue-70-root-schema-constructor COMMAND issue-70-root-schema-constructor)
|
||||
|
||||
add_executable(issue-25-default-values issue-25-default-values.cpp)
|
||||
target_link_libraries(issue-25-default-values nlohmann_json_schema_validator)
|
||||
add_test(NAME issue-25-default-values COMMAND issue-25-default-values)
|
||||
|
||||
# Unit test for string format checks
|
||||
add_executable("string-format-check-test" "string-format-check-test.cpp")
|
||||
target_include_directories("string-format-check-test" PRIVATE "${PROJECT_SOURCE_DIR}/src/")
|
||||
|
||||
100
test/issue-25-default-values.cpp
Normal file
100
test/issue-25-default-values.cpp
Normal file
@ -0,0 +1,100 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json-schema.hpp>
|
||||
|
||||
using nlohmann::json;
|
||||
using nlohmann::json_schema::json_validator;
|
||||
|
||||
static const json person_schema = R"(
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "A person",
|
||||
"properties": {
|
||||
"name": {
|
||||
"description": "Name",
|
||||
"type": "string"
|
||||
},
|
||||
"age": {
|
||||
"description": "Age of the person",
|
||||
"type": "number",
|
||||
"minimum": 2,
|
||||
"maximum": 200
|
||||
},
|
||||
"address":{
|
||||
"type": "object",
|
||||
"properties":{
|
||||
"street":{
|
||||
"type": "string",
|
||||
"default": "Abbey Road"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"age"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"type": "object"
|
||||
})"_json;
|
||||
|
||||
int main(void)
|
||||
{
|
||||
json_validator validator{};
|
||||
|
||||
// add address which is optional that should generate a diff containing a default street
|
||||
json person_missing_address = R"({
|
||||
"name": "Hans",
|
||||
"age": 69,
|
||||
"address": {}
|
||||
})"_json;
|
||||
|
||||
validator.set_root_schema(person_schema);
|
||||
|
||||
const auto default_patch = validator.validate(person_missing_address);
|
||||
|
||||
if (!default_patch.is_array()) {
|
||||
std::cerr << "Patch with defaults is expected to be an array" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (default_patch.size() != 1) {
|
||||
std::cerr << "Patch with defaults is expected to contain one opperation" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
const auto &single_op = default_patch[0];
|
||||
|
||||
if (!single_op.contains("op")) {
|
||||
std::cerr << "Patch with defaults is expected to contain opperation entry" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (single_op["op"].get<std::string>() != "add") {
|
||||
std::cerr << "Patch with defaults is expected to contain add opperation" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!single_op.contains("path")) {
|
||||
std::cerr << "Patch with defaults is expected to contain a path" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
const auto &readPath = single_op["path"].get<std::string>();
|
||||
if (readPath != "/address/street") {
|
||||
std::cerr << "Patch with defaults contains wrong path. It is " << readPath << " and should be "
|
||||
<< "/address/street" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!single_op.contains("value")) {
|
||||
std::cerr << "Patch with defaults is expected to contain a value" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (single_op["value"].get<std::string>() != "Abbey Road") {
|
||||
std::cerr << "Patch with defaults contains wrong value" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user