From a3f3a59a0ec29e5e4ec6467cf4c0b6ee82af1812 Mon Sep 17 00:00:00 2001
From: Patrick Boettcher
Date: Sat, 23 May 2020 10:14:10 +0200
Subject: [PATCH] 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);