add support for validate binary data in nlohmann::json
Used for bson or other implementations of binary json-instances.
This commit is contained in:
parent
a3f3a59a0e
commit
06540818c3
@ -472,7 +472,8 @@ class type_schema : public schema
|
|||||||
}
|
}
|
||||||
|
|
||||||
// special treatment
|
// 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)
|
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_) + "'");
|
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 {
|
else {
|
||||||
@ -613,6 +614,27 @@ public:
|
|||||||
if (attr != sch.end()) {
|
if (attr != sch.end()) {
|
||||||
std::get<0>(content_) = true;
|
std::get<0>(content_) = true;
|
||||||
std::get<1>(content_) = attr.value().get<std::string>();
|
std::get<1>(content_) = attr.value().get<std::string>();
|
||||||
|
|
||||||
|
// 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);
|
sch.erase(attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1154,6 +1176,14 @@ std::shared_ptr<schema> type_schema::make(json &schema,
|
|||||||
|
|
||||||
case json::value_t::discarded: // not a real type - silence please
|
case json::value_t::discarded: // not a real type - silence please
|
||||||
break;
|
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<boolean>(tmp, root); // always true - content-check with do the work
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -55,3 +55,8 @@ add_executable(json-patch json-patch.cpp)
|
|||||||
target_include_directories(json-patch PRIVATE ${PROJECT_SOURCE_DIR}/src)
|
target_include_directories(json-patch PRIVATE ${PROJECT_SOURCE_DIR}/src)
|
||||||
target_link_libraries(json-patch nlohmann_json_schema_validator)
|
target_link_libraries(json-patch nlohmann_json_schema_validator)
|
||||||
add_test(NAME json-patch COMMAND json-patch)
|
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)
|
||||||
|
|||||||
182
test/binary-validation.cpp
Normal file
182
test/binary-validation.cpp
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
// bson-validate.cpp
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <nlohmann/json-schema.hpp>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
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<nlohmann::json::json_pointer> 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;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user