From 80333cda2bdae59edd4731bc7b2a91db3ef4e635 Mon Sep 17 00:00:00 2001
From: Patrick Boettcher
Date: Sat, 23 May 2020 09:04:42 +0200
Subject: [PATCH 1/5] bump to 2.1.1 for new developments
---
CMakeLists.txt | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c4cc218..aae7ab3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,12 +1,12 @@
project(nlohmann_json_schema_validator
- LANGUAGES CXX)
+ LANGUAGES CXX)
-set(PROJECT_VERSION 2.1.0)
+set(PROJECT_VERSION 2.1.1)
cmake_minimum_required(VERSION 3.2)
-option(BUILD_TESTS "Build tests" ON)
-option(BUILD_EXAMPLES "Build examples" ON)
+option(BUILD_TESTS "Build tests" ON)
+option(BUILD_EXAMPLES "Build examples" ON)
# the library
add_library(nlohmann_json_schema_validator
From a3f3a59a0ec29e5e4ec6467cf4c0b6ee82af1812 Mon Sep 17 00:00:00 2001
From: Patrick Boettcher
Date: Sat, 23 May 2020 10:14:10 +0200
Subject: [PATCH 2/5] add content-checker-callback
Used when contentEncoding or mediaType attributes are present
on a string-value.
---
src/json-validator.cpp | 69 ++++++++++++++++---
src/nlohmann/json-schema.hpp | 7 +-
test/JSON-Schema-Test-Suite/CMakeLists.txt | 1 -
.../json-schema-test.cpp | 47 ++++++++++++-
4 files changed, 111 insertions(+), 13 deletions(-)
diff --git a/src/json-validator.cpp b/src/json-validator.cpp
index f16f3ef..d956037 100644
--- a/src/json-validator.cpp
+++ b/src/json-validator.cpp
@@ -105,6 +105,7 @@ class root_schema : public schema
{
schema_loader loader_;
format_checker format_check_;
+ content_checker content_check_;
std::shared_ptr root_;
@@ -128,10 +129,18 @@ class root_schema : public schema
public:
root_schema(schema_loader &&loader,
- format_checker &&format)
- : schema(this), loader_(std::move(loader)), format_check_(std::move(format)) {}
+ format_checker &&format,
+ content_checker &&content)
+
+ : schema(this),
+ loader_(std::move(loader)),
+ format_check_(std::move(format)),
+ content_check_(std::move(content))
+ {
+ }
format_checker &format_check() { return format_check_; }
+ content_checker &content_check() { return content_check_; }
void insert(const json_uri &uri, const std::shared_ptr &s)
{
@@ -461,8 +470,24 @@ class type_schema : public schema
else_->validate(ptr, instance, patch, e);
}
}
+
+ // special treatment
+ if (instance.type() == json::value_t::string && std::get<0>(content_)) {
+ if (root_->content_check() == nullptr)
+ e.error(ptr, instance, std::string("a content checker was not provided but a contentEncoding or contentMediaType for this string have been present: '") + std::get<1>(content_) + "' '" + std::get<2>(content_) + "'");
+ else {
+ try {
+ root_->content_check()(std::get<1>(content_), std::get<2>(content_), instance);
+ } catch (const std::exception &ex) {
+ e.error(ptr, instance, std::string("content-checking failed: ") + ex.what());
+ }
+ }
+ }
}
+protected:
+ std::tuple content_{false, "", ""};
+
public:
type_schema(json &sch,
root_schema *root,
@@ -583,6 +608,20 @@ public:
}
sch.erase(attr);
}
+
+ attr = sch.find("contentEncoding");
+ if (attr != sch.end()) {
+ std::get<0>(content_) = true;
+ std::get<1>(content_) = attr.value().get();
+ sch.erase(attr);
+ }
+
+ attr = sch.find("contentMediaType");
+ if (attr != sch.end()) {
+ std::get<0>(content_) = true;
+ std::get<2>(content_) = attr.value().get();
+ sch.erase(attr);
+ }
}
};
@@ -1209,19 +1248,33 @@ namespace json_schema
{
json_validator::json_validator(schema_loader loader,
- format_checker format)
- : root_(std::unique_ptr(new root_schema(std::move(loader), std::move(format))))
+ format_checker format,
+ content_checker content)
+ : root_(std::unique_ptr(new root_schema(std::move(loader),
+ std::move(format),
+ std::move(content))))
{
}
-json_validator::json_validator(const json &schema, schema_loader loader, format_checker format)
- : json_validator(std::move(loader), std::move(format))
+json_validator::json_validator(const json &schema,
+ schema_loader loader,
+ format_checker format,
+ content_checker content)
+ : json_validator(std::move(loader),
+ std::move(format),
+ std::move(content))
{
set_root_schema(schema);
}
-json_validator::json_validator(json &&schema, schema_loader loader, format_checker format)
- : json_validator(std::move(loader), std::move(format))
+json_validator::json_validator(json &&schema,
+ schema_loader loader,
+ format_checker format,
+ content_checker content)
+
+ : json_validator(std::move(loader),
+ std::move(format),
+ std::move(content))
{
set_root_schema(std::move(schema));
}
diff --git a/src/nlohmann/json-schema.hpp b/src/nlohmann/json-schema.hpp
index baa9719..c25ca72 100644
--- a/src/nlohmann/json-schema.hpp
+++ b/src/nlohmann/json-schema.hpp
@@ -133,6 +133,7 @@ extern json draft7_schema_builtin;
typedef std::function schema_loader;
typedef std::function format_checker;
+typedef std::function content_checker;
// Interface for validation error handlers
class JSON_SCHEMA_VALIDATOR_API error_handler
@@ -168,10 +169,10 @@ class JSON_SCHEMA_VALIDATOR_API json_validator
std::unique_ptr root_;
public:
- json_validator(schema_loader = nullptr, format_checker = nullptr);
+ json_validator(schema_loader = nullptr, format_checker = nullptr, content_checker = nullptr);
- json_validator(const json &, schema_loader = nullptr, format_checker = nullptr);
- json_validator(json &&, schema_loader = nullptr, format_checker = nullptr);
+ json_validator(const json &, schema_loader = nullptr, format_checker = nullptr, content_checker = nullptr);
+ json_validator(json &&, schema_loader = nullptr, format_checker = nullptr, content_checker = nullptr);
json_validator(json_validator &&);
json_validator &operator=(json_validator &&);
diff --git a/test/JSON-Schema-Test-Suite/CMakeLists.txt b/test/JSON-Schema-Test-Suite/CMakeLists.txt
index fc52df2..61d5fd4 100644
--- a/test/JSON-Schema-Test-Suite/CMakeLists.txt
+++ b/test/JSON-Schema-Test-Suite/CMakeLists.txt
@@ -50,7 +50,6 @@ if(JSON_SCHEMA_TEST_SUITE_PATH)
# some optional tests will fail
set_tests_properties(
JSON-Suite::Optional::bignum
- JSON-Suite::Optional::content
JSON-Suite::Optional::zeroTerminatedFloats
JSON-Suite::Optional::non-bmp-regex
diff --git a/test/JSON-Schema-Test-Suite/json-schema-test.cpp b/test/JSON-Schema-Test-Suite/json-schema-test.cpp
index 05a255d..deee4c8 100644
--- a/test/JSON-Schema-Test-Suite/json-schema-test.cpp
+++ b/test/JSON-Schema-Test-Suite/json-schema-test.cpp
@@ -39,6 +39,50 @@ static void loader(const json_uri &uri, json &schema)
}
}
+// from here
+// https://stackoverflow.com/a/34571089/880584
+static std::string base64_decode(const std::string &in)
+{
+ std::string out;
+
+ std::vector T(256, -1);
+ for (int i = 0; i < 64; i++)
+ T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i;
+
+ unsigned val = 0;
+ int valb = -8;
+ for (uint8_t c : in) {
+ if (c == '=')
+ break;
+
+ if (T[c] == -1) {
+ throw std::invalid_argument("base64-decode: unexpected character in encode string: '" + std::string(1, c) + "'");
+ }
+ val = (val << 6) + T[c];
+ valb += 6;
+ if (valb >= 0) {
+ out.push_back(char((val >> valb) & 0xFF));
+ valb -= 8;
+ }
+ }
+ return out;
+}
+
+static void content(const std::string &contentEncoding, const std::string &contentMediaType, const json &instance)
+{
+ std::string content = instance;
+
+ if (contentEncoding == "base64")
+ content = base64_decode(instance);
+ else if (contentEncoding != "")
+ throw std::invalid_argument("unable to check for contentEncoding '" + contentEncoding + "'");
+
+ if (contentMediaType == "application/json")
+ auto dummy = json::parse(content); // throws if conversion fails
+ else if (contentMediaType != "")
+ throw std::invalid_argument("unable to check for contentMediaType '" + contentMediaType + "'");
+}
+
int main(void)
{
json validation; // a validation case following the JSON-test-suite-schema
@@ -62,7 +106,8 @@ int main(void)
const auto &schema = test_group["schema"];
json_validator validator(loader,
- nlohmann::json_schema::default_string_format_check);
+ nlohmann::json_schema::default_string_format_check,
+ content);
validator.set_root_schema(schema);
From 06540818c32b69279f9a65c07e236564043b8c40 Mon Sep 17 00:00:00 2001
From: andrejlevkovitch
Date: Fri, 22 May 2020 11:50:26 +0300
Subject: [PATCH 3/5] add support for validate binary data in nlohmann::json
Used for bson or other implementations of binary json-instances.
---
src/json-validator.cpp | 32 ++++++-
test/CMakeLists.txt | 5 +
test/binary-validation.cpp | 182 +++++++++++++++++++++++++++++++++++++
3 files changed, 218 insertions(+), 1 deletion(-)
create mode 100644 test/binary-validation.cpp
diff --git a/src/json-validator.cpp b/src/json-validator.cpp
index d956037..668390a 100644
--- a/src/json-validator.cpp
+++ b/src/json-validator.cpp
@@ -472,7 +472,8 @@ class type_schema : public schema
}
// special treatment
- if (instance.type() == json::value_t::string && std::get<0>(content_)) {
+ if ((instance.type() == json::value_t::string || instance.type() == json::value_t::binary) &&
+ std::get<0>(content_)) {
if (root_->content_check() == nullptr)
e.error(ptr, instance, std::string("a content checker was not provided but a contentEncoding or contentMediaType for this string have been present: '") + std::get<1>(content_) + "' '" + std::get<2>(content_) + "'");
else {
@@ -613,6 +614,27 @@ public:
if (attr != sch.end()) {
std::get<0>(content_) = true;
std::get<1>(content_) = attr.value().get();
+
+ // special case for nlohmann::json-binary-types
+ //
+ // https://github.com/pboettch/json-schema-validator/pull/114
+ //
+ // We cannot use explicitly in a schema: {"type": "binary"} or
+ // "type": ["binary", "number"] we have to be implicit. For a
+ // schema where "contentEncoding" is set to "binary", an instance
+ // of type json::value_t::binary is accepted. If a
+ // contentEncoding-callback has to be provided and is called
+ // accordingly. For encoding=binary, no other type validations are done
+
+ if (attr.value() == "binary") {
+ // clear out all other type-schemas
+ for (auto &type_valid : type_)
+ type_valid = nullptr;
+
+ // when no schema-type is explicitly given, we accept binary-values
+ type_[(uint8_t) json::value_t::binary] = type_schema::make(sch, json::value_t::binary, root, uris, known_keywords);
+ }
+
sch.erase(attr);
}
@@ -1154,6 +1176,14 @@ std::shared_ptr type_schema::make(json &schema,
case json::value_t::discarded: // not a real type - silence please
break;
+
+ case json::value_t::binary: {
+ // can be used for validate bson or other binary representation of json
+ // - specific to nlohmann::json - this type is not standardized
+ // json-schema-draft-7
+ json tmp = true;
+ return std::make_shared(tmp, root); // always true - content-check with do the work
+ }
}
return nullptr;
}
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 8d67e90..f268219 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -55,3 +55,8 @@ add_executable(json-patch json-patch.cpp)
target_include_directories(json-patch PRIVATE ${PROJECT_SOURCE_DIR}/src)
target_link_libraries(json-patch nlohmann_json_schema_validator)
add_test(NAME json-patch COMMAND json-patch)
+
+add_executable(binary-validation binary-validation.cpp)
+target_include_directories(binary-validation PRIVATE ${PROJECT_SOURCE_DIR}/src)
+target_link_libraries(binary-validation PRIVATE nlohmann_json_schema_validator)
+add_test(NAME binary-validation COMMAND binary-validation)
diff --git a/test/binary-validation.cpp b/test/binary-validation.cpp
new file mode 100644
index 0000000..d5ae031
--- /dev/null
+++ b/test/binary-validation.cpp
@@ -0,0 +1,182 @@
+// bson-validate.cpp
+
+#include
+#include
+#include
+
+static int error_count = 0;
+
+#define EXPECT_EQ(a, b) \
+ do { \
+ if (a != b) { \
+ std::cerr << "Failed: '" << a << "' != '" << b << "'\n"; \
+ error_count++; \
+ } \
+ } while (0)
+
+using json = nlohmann::json;
+using validator = nlohmann::json_schema::json_validator;
+
+// check binary data validation
+const json bson_schema = json::parse(R"(
+{
+ "type": "object",
+ "properties": {
+ "standard_string": {
+ "type": "string"
+ },
+ "binary_data": {
+ "type": "string",
+ "contentEncoding": "binary"
+ }
+ },
+ "additionalProperties": false
+}
+)");
+
+const json array_of_types = json::parse(R"(
+{
+ "type": "object",
+ "properties": {
+ "something": {
+ "type": ["string", "number", "boolean"],
+ "contentEncoding": "binary"
+ }
+ }
+}
+)");
+
+const json array_of_types_without_binary = json::parse(R"(
+{
+ "type": "object",
+ "properties": {
+ "something": {
+ "type": ["string", "number", "boolean"]
+ }
+ }
+}
+)");
+
+class store_ptr_err_handler : public nlohmann::json_schema::basic_error_handler
+{
+ void error(const nlohmann::json::json_pointer &ptr, const json &, const std::string &message) override
+ {
+ nlohmann::json_schema::basic_error_handler::error(ptr, "", message);
+ std::cerr << "ERROR: '" << ptr << "' - '"
+ << ""
+ << "': " << message << "\n";
+ failed_pointers.push_back(ptr);
+ }
+
+public:
+ std::vector failed_pointers;
+
+ void reset() override
+ {
+ nlohmann::json_schema::basic_error_handler::reset();
+ failed_pointers.clear();
+ }
+};
+
+static void content(const std::string &contentEncoding, const std::string &contentMediaType, const json &instance)
+{
+ if (instance.type() != json::value_t::binary)
+ throw std::invalid_argument("invalid instance type for binary content checker");
+
+ std::cerr << "mediaType: '" << contentMediaType << "', encoding: '" << contentEncoding << "'\n";
+
+ if (contentMediaType != "")
+ throw std::invalid_argument("unable to check for contentMediaType " + contentMediaType);
+
+ if (contentEncoding == "binary") {
+
+ } else if (contentEncoding != "")
+ throw std::invalid_argument("unable to check for contentEncoding " + contentEncoding);
+}
+
+int main()
+{
+ validator val(nullptr, nullptr, content);
+
+ // create some bson doc
+ json::binary_t arr;
+ std::string as_binary = "hello world";
+ std::copy(as_binary.begin(), as_binary.end(), std::back_inserter(arr));
+
+ json binary = json::binary(arr);
+
+ store_ptr_err_handler err;
+
+ /////////////////////////////////////
+ val.set_root_schema(bson_schema);
+
+ // all right
+ val.validate({{"standard_string", "some string"}, {"binary_data", binary}}, err);
+ EXPECT_EQ(err.failed_pointers.size(), 0);
+ err.reset();
+
+ // invalid binary data
+ val.validate({{"binary_data", "string, but expect binary data"}}, err);
+ EXPECT_EQ(err.failed_pointers.size(), 2);
+ EXPECT_EQ(err.failed_pointers[0].to_string(), "/binary_data");
+ EXPECT_EQ(err.failed_pointers[1].to_string(), "/binary_data"); // second error comes from content-checker
+ err.reset();
+
+ // also check that simple string not accept binary data
+ val.validate({{"standard_string", binary}, {"binary_data", binary}}, err);
+ EXPECT_EQ(err.failed_pointers.size(), 1);
+ EXPECT_EQ(err.failed_pointers[0].to_string(), "/standard_string");
+ err.reset();
+
+ /////////////////////////////////////
+ // check with array of types
+
+ // check simple types
+ val.set_root_schema(array_of_types);
+ val.validate({{"something", "string"}}, err);
+ val.validate({{"something", 1}}, err);
+ val.validate({{"something", false}}, err);
+ EXPECT_EQ(err.failed_pointers.size(), 4); // binary encoding invalidated all other types
+ err.reset();
+
+ // check binary data
+ val.validate({{"something", binary}}, err);
+ EXPECT_EQ(err.failed_pointers.size(), 0);
+ err.reset();
+
+ /////////////////////////////////////
+ // and check that you can't set binary data if contentEncoding don't set
+ val.set_root_schema(array_of_types_without_binary);
+ val.validate({{"something", binary}}, err);
+ EXPECT_EQ(err.failed_pointers.size(), 1);
+ EXPECT_EQ(err.failed_pointers[0], "/something");
+ err.reset();
+
+ // check without content-callback everything fails
+ validator val_no_content;
+
+ /////////////////////////////////////
+ val_no_content.set_root_schema(bson_schema);
+
+ // all right
+ val_no_content.validate({{"standard_string", "some string"}, {"binary_data", binary}}, err);
+ EXPECT_EQ(err.failed_pointers.size(), 1);
+ err.reset();
+
+ // invalid binary data
+ val_no_content.validate({{"binary_data", "string, but expect binary data"}}, err);
+ EXPECT_EQ(err.failed_pointers.size(), 2);
+ EXPECT_EQ(err.failed_pointers[0].to_string(), "/binary_data");
+ EXPECT_EQ(err.failed_pointers[1].to_string(), "/binary_data"); // second error comes from content-checker
+ err.reset();
+
+ // also check that simple string not accept binary data
+ val_no_content.validate({{"standard_string", binary}, {"binary_data", binary}}, err);
+ EXPECT_EQ(err.failed_pointers.size(), 2);
+ EXPECT_EQ(err.failed_pointers[0].to_string(), "/binary_data");
+ EXPECT_EQ(err.failed_pointers[1].to_string(), "/standard_string");
+ err.reset();
+
+
+ return error_count;
+}
From 86e97231bc3549b767040166f794ca0400b42672 Mon Sep 17 00:00:00 2001
From: andrejlevkovitch
Date: Fri, 29 May 2020 16:21:01 +0300
Subject: [PATCH 4/5] move checks contentEncoding and contentMediaType to
string schema
---
src/json-validator.cpp | 114 ++++++++++++++++++-------------------
test/binary-validation.cpp | 31 +++++-----
2 files changed, 69 insertions(+), 76 deletions(-)
diff --git a/src/json-validator.cpp b/src/json-validator.cpp
index 668390a..8fc6f6f 100644
--- a/src/json-validator.cpp
+++ b/src/json-validator.cpp
@@ -470,25 +470,8 @@ class type_schema : public schema
else_->validate(ptr, instance, patch, e);
}
}
-
- // special treatment
- if ((instance.type() == json::value_t::string || instance.type() == json::value_t::binary) &&
- std::get<0>(content_)) {
- if (root_->content_check() == nullptr)
- e.error(ptr, instance, std::string("a content checker was not provided but a contentEncoding or contentMediaType for this string have been present: '") + std::get<1>(content_) + "' '" + std::get<2>(content_) + "'");
- else {
- try {
- root_->content_check()(std::get<1>(content_), std::get<2>(content_), instance);
- } catch (const std::exception &ex) {
- e.error(ptr, instance, std::string("content-checking failed: ") + ex.what());
- }
- }
- }
}
-protected:
- std::tuple content_{false, "", ""};
-
public:
type_schema(json &sch,
root_schema *root,
@@ -553,6 +536,11 @@ public:
// we stick with JSON-schema: use the integer-validator if instance-value is unsigned
type_[(uint8_t) json::value_t::number_unsigned] = type_[(uint8_t) json::value_t::number_integer];
+ // special for binary types
+ if (type_[(uint8_t) json::value_t::string]) {
+ type_[(uint8_t) json::value_t::binary] = type_[(uint8_t) json::value_t::string];
+ }
+
attr = sch.find("enum");
if (attr != sch.end()) {
enum_ = {true, attr.value()};
@@ -609,41 +597,6 @@ public:
}
sch.erase(attr);
}
-
- attr = sch.find("contentEncoding");
- if (attr != sch.end()) {
- std::get<0>(content_) = true;
- std::get<1>(content_) = attr.value().get();
-
- // special case for nlohmann::json-binary-types
- //
- // https://github.com/pboettch/json-schema-validator/pull/114
- //
- // We cannot use explicitly in a schema: {"type": "binary"} or
- // "type": ["binary", "number"] we have to be implicit. For a
- // schema where "contentEncoding" is set to "binary", an instance
- // of type json::value_t::binary is accepted. If a
- // contentEncoding-callback has to be provided and is called
- // accordingly. For encoding=binary, no other type validations are done
-
- if (attr.value() == "binary") {
- // clear out all other type-schemas
- for (auto &type_valid : type_)
- type_valid = nullptr;
-
- // when no schema-type is explicitly given, we accept binary-values
- type_[(uint8_t) json::value_t::binary] = type_schema::make(sch, json::value_t::binary, root, uris, known_keywords);
- }
-
- sch.erase(attr);
- }
-
- attr = sch.find("contentMediaType");
- if (attr != sch.end()) {
- std::get<0>(content_) = true;
- std::get<2>(content_) = attr.value().get();
- sch.erase(attr);
- }
}
};
@@ -658,11 +611,12 @@ class string : public schema
#endif
std::pair format_;
+ std::tuple content_{false, "", ""};
std::size_t utf8_length(const std::string &s) const
{
size_t len = 0;
- for (const unsigned char &c : s)
+ for (unsigned char c : s)
if ((c & 0xc0) != 0x80)
len++;
return len;
@@ -686,6 +640,24 @@ class string : public schema
}
}
+ if (std::get<0>(content_)) {
+ if (root_->content_check() == nullptr)
+ e.error(ptr, instance, std::string("a content checker was not provided but a contentEncoding or contentMediaType for this string have been present: '") + std::get<1>(content_) + "' '" + std::get<2>(content_) + "'");
+ else {
+ try {
+ root_->content_check()(std::get<1>(content_), std::get<2>(content_), instance);
+ } catch (const std::exception &ex) {
+ e.error(ptr, instance, std::string("content-checking failed: ") + ex.what());
+ }
+ }
+ } else if (instance.type() == json::value_t::binary) {
+ e.error(ptr, instance, "expected string, but get binary data");
+ }
+
+ if (instance.type() != json::value_t::string) {
+ return; // next checks only for strings
+ }
+
#ifndef NO_STD_REGEX
if (pattern_.first &&
!REGEX_NAMESPACE::regex_search(instance.get(), pattern_.second))
@@ -721,6 +693,33 @@ public:
sch.erase(attr);
}
+ attr = sch.find("contentEncoding");
+ if (attr != sch.end()) {
+ std::get<0>(content_) = true;
+ std::get<1>(content_) = attr.value().get();
+
+ // special case for nlohmann::json-binary-types
+ //
+ // https://github.com/pboettch/json-schema-validator/pull/114
+ //
+ // We cannot use explicitly in a schema: {"type": "binary"} or
+ // "type": ["binary", "number"] we have to be implicit. For a
+ // schema where "contentEncoding" is set to "binary", an instance
+ // of type json::value_t::binary is accepted. If a
+ // contentEncoding-callback has to be provided and is called
+ // accordingly. For encoding=binary, no other type validations are done
+
+ sch.erase(attr);
+ }
+
+ attr = sch.find("contentMediaType");
+ if (attr != sch.end()) {
+ std::get<0>(content_) = true;
+ std::get<2>(content_) = attr.value().get();
+
+ sch.erase(attr);
+ }
+
#ifndef NO_STD_REGEX
attr = sch.find("pattern");
if (attr != sch.end()) {
@@ -1177,13 +1176,8 @@ std::shared_ptr type_schema::make(json &schema,
case json::value_t::discarded: // not a real type - silence please
break;
- case json::value_t::binary: {
- // can be used for validate bson or other binary representation of json
- // - specific to nlohmann::json - this type is not standardized
- // json-schema-draft-7
- json tmp = true;
- return std::make_shared(tmp, root); // always true - content-check with do the work
- }
+ case json::value_t::binary:
+ break;
}
return nullptr;
}
diff --git a/test/binary-validation.cpp b/test/binary-validation.cpp
index d5ae031..fda5afa 100644
--- a/test/binary-validation.cpp
+++ b/test/binary-validation.cpp
@@ -80,18 +80,17 @@ public:
static void content(const std::string &contentEncoding, const std::string &contentMediaType, const json &instance)
{
- if (instance.type() != json::value_t::binary)
- throw std::invalid_argument("invalid instance type for binary content checker");
-
std::cerr << "mediaType: '" << contentMediaType << "', encoding: '" << contentEncoding << "'\n";
- if (contentMediaType != "")
- throw std::invalid_argument("unable to check for contentMediaType " + contentMediaType);
-
if (contentEncoding == "binary") {
-
- } else if (contentEncoding != "")
- throw std::invalid_argument("unable to check for contentEncoding " + contentEncoding);
+ if (instance.type() != json::value_t::binary) {
+ throw std::invalid_argument{"expected binary data"};
+ }
+ } else {
+ if (instance.type() == json::value_t::binary) {
+ throw std::invalid_argument{"expected string, but get binary"};
+ }
+ }
}
int main()
@@ -117,9 +116,8 @@ int main()
// invalid binary data
val.validate({{"binary_data", "string, but expect binary data"}}, err);
- EXPECT_EQ(err.failed_pointers.size(), 2);
+ EXPECT_EQ(err.failed_pointers.size(), 1);
EXPECT_EQ(err.failed_pointers[0].to_string(), "/binary_data");
- EXPECT_EQ(err.failed_pointers[1].to_string(), "/binary_data"); // second error comes from content-checker
err.reset();
// also check that simple string not accept binary data
@@ -133,10 +131,13 @@ int main()
// check simple types
val.set_root_schema(array_of_types);
- val.validate({{"something", "string"}}, err);
val.validate({{"something", 1}}, err);
val.validate({{"something", false}}, err);
- EXPECT_EQ(err.failed_pointers.size(), 4); // binary encoding invalidated all other types
+ // TODO when we set `string` in array and set `contentEncoding` = "binary" - what it means? We expected string or binary?
+ // Or we expect only binary? Now if you set `contentEncoding` = "binary", then it means that you expect only binary data,
+ // not string
+ //val.validate({{"something", "string"}}, err); -> produce error about type
+ EXPECT_EQ(err.failed_pointers.size(), 0);
err.reset();
// check binary data
@@ -165,9 +166,8 @@ int main()
// invalid binary data
val_no_content.validate({{"binary_data", "string, but expect binary data"}}, err);
- EXPECT_EQ(err.failed_pointers.size(), 2);
+ EXPECT_EQ(err.failed_pointers.size(), 1);
EXPECT_EQ(err.failed_pointers[0].to_string(), "/binary_data");
- EXPECT_EQ(err.failed_pointers[1].to_string(), "/binary_data"); // second error comes from content-checker
err.reset();
// also check that simple string not accept binary data
@@ -177,6 +177,5 @@ int main()
EXPECT_EQ(err.failed_pointers[1].to_string(), "/standard_string");
err.reset();
-
return error_count;
}
From f46940990e807337483fffc85c8b92e29e7b145a Mon Sep 17 00:00:00 2001
From: andrejlevkovitch
Date: Fri, 29 May 2020 17:05:54 +0300
Subject: [PATCH 5/5] add checks about content checker at shema parsing stage
---
src/json-validator.cpp | 4 ++++
test/binary-validation.cpp | 36 +++++++++++++++---------------------
2 files changed, 19 insertions(+), 21 deletions(-)
diff --git a/src/json-validator.cpp b/src/json-validator.cpp
index 8fc6f6f..9c5fc26 100644
--- a/src/json-validator.cpp
+++ b/src/json-validator.cpp
@@ -720,6 +720,10 @@ public:
sch.erase(attr);
}
+ if (std::get<0>(content_) == true && root_->content_check() == nullptr) {
+ throw std::invalid_argument{"schema contains contentEncoding/contentMediaType but content checker was not set"};
+ }
+
#ifndef NO_STD_REGEX
attr = sch.find("pattern");
if (attr != sch.end()) {
diff --git a/test/binary-validation.cpp b/test/binary-validation.cpp
index fda5afa..01392cf 100644
--- a/test/binary-validation.cpp
+++ b/test/binary-validation.cpp
@@ -14,6 +14,19 @@ static int error_count = 0;
} \
} while (0)
+#define EXPECT_THROW(foo) \
+ { \
+ bool ok = false; \
+ try { \
+ foo; \
+ } catch (std::exception &) { \
+ ok = true; \
+ } \
+ if (ok == false) { \
+ error_count++; \
+ } \
+ }
+
using json = nlohmann::json;
using validator = nlohmann::json_schema::json_validator;
@@ -153,29 +166,10 @@ int main()
EXPECT_EQ(err.failed_pointers[0], "/something");
err.reset();
- // check without content-callback everything fails
+ // check that without content callback you get exception with schema with contentEncoding or contentMeditType
validator val_no_content;
- /////////////////////////////////////
- val_no_content.set_root_schema(bson_schema);
-
- // all right
- val_no_content.validate({{"standard_string", "some string"}, {"binary_data", binary}}, err);
- EXPECT_EQ(err.failed_pointers.size(), 1);
- err.reset();
-
- // invalid binary data
- val_no_content.validate({{"binary_data", "string, but expect binary data"}}, err);
- EXPECT_EQ(err.failed_pointers.size(), 1);
- EXPECT_EQ(err.failed_pointers[0].to_string(), "/binary_data");
- err.reset();
-
- // also check that simple string not accept binary data
- val_no_content.validate({{"standard_string", binary}, {"binary_data", binary}}, err);
- EXPECT_EQ(err.failed_pointers.size(), 2);
- EXPECT_EQ(err.failed_pointers[0].to_string(), "/binary_data");
- EXPECT_EQ(err.failed_pointers[1].to_string(), "/standard_string");
- err.reset();
+ EXPECT_THROW(val_no_content.set_root_schema(bson_schema));
return error_count;
}