MQTTSuite
Loading...
Searching...
No Matches
json-validator.cpp
Go to the documentation of this file.
1/*
2 * JSON schema validator for JSON for modern C++
3 *
4 * Copyright (c) 2016-2019 Patrick Boettcher <p@yai.se>.
5 *
6 * SPDX-License-Identifier: MIT
7 *
8 */
9#include <nlohmann/json-schema.hpp>
10
11#include "json-patch.hpp"
12
13#include <deque>
14#include <memory>
15#include <set>
16#include <sstream>
17#include <string>
18
19using nlohmann::json;
20using nlohmann::json_patch;
21using nlohmann::json_uri;
22using nlohmann::json_schema::root_schema;
23using namespace nlohmann::json_schema;
24
25#ifdef JSON_SCHEMA_BOOST_REGEX
26# include <boost/regex.hpp>
27# define REGEX_NAMESPACE boost
28#elif defined(JSON_SCHEMA_NO_REGEX)
29# define NO_STD_REGEX
30#else
31# include <regex>
32# define REGEX_NAMESPACE std
33#endif
34
35namespace
36{
37
38class schema
39{
40protected:
42 json default_value_ = nullptr;
43
44protected:
45 virtual std::shared_ptr<schema> make_for_default_(
46 std::shared_ptr<::schema> & /* sch */,
47 root_schema * /* root */,
48 std::vector<nlohmann::json_uri> & /* uris */,
49 nlohmann::json & /* default_value */) const
50 {
51 return nullptr;
52 };
53
54public:
55 virtual ~schema() = default;
56
58 : root_(root) {}
59
60 virtual void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const = 0;
61
62 virtual const json &default_value(const json::json_pointer &, const json &, error_handler &) const
63 {
64 return default_value_;
65 }
66
67 void set_default_value(const json &v) { default_value_ = v; }
68
69 static std::shared_ptr<schema> make(json &schema,
70 root_schema *root,
71 const std::vector<std::string> &key,
72 std::vector<nlohmann::json_uri> uris);
73};
74
75class schema_ref : public schema
76{
77 const std::string id_;
78 std::weak_ptr<schema> target_;
79 std::shared_ptr<schema> target_strong_; // for references to references keep also the shared_ptr because
80 // no one else might use it after resolving
81
82 void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const final
83 {
84 auto target = target_.lock();
85
86 if (target)
87 target->validate(ptr, instance, patch, e);
88 else
89 e.error(ptr, instance, "unresolved or freed schema-reference " + id_);
90 }
91
92 const json &default_value(const json::json_pointer &ptr, const json &instance, error_handler &e) const override final
93 {
94 if (!default_value_.is_null())
95 return default_value_;
96
97 auto target = target_.lock();
98 if (target)
99 return target->default_value(ptr, instance, e);
100
101 e.error(ptr, instance, "unresolved or freed schema-reference " + id_);
102
103 return default_value_;
104 }
105
106protected:
107 virtual std::shared_ptr<schema> make_for_default_(
108 std::shared_ptr<::schema> &sch,
109 root_schema *root,
110 std::vector<nlohmann::json_uri> &uris,
111 nlohmann::json &default_value) const override
112 {
113 // create a new reference schema using the original reference (which will be resolved later)
114 // to store this overloaded default value #209
115 auto result = std::make_shared<schema_ref>(uris[0].to_string(), root);
116 result->set_target(sch, true);
117 result->set_default_value(default_value);
118 return result;
119 };
120
121public:
122 schema_ref(const std::string &id, root_schema *root)
123 : schema(root), id_(id) {}
124
125 const std::string &id() const { return id_; }
126
127 void set_target(const std::shared_ptr<schema> &target, bool strong = false)
128 {
129 target_ = target;
130 if (strong)
131 target_strong_ = target;
132 }
133};
134
135} // namespace
136
137namespace nlohmann
138{
139namespace json_schema
140{
141
143{
147
148 std::shared_ptr<schema> root_;
149
150 struct schema_file {
151 std::map<std::string, std::shared_ptr<schema>> schemas;
152 std::map<std::string, std::shared_ptr<schema_ref>> unresolved; // contains all unresolved references from any other file seen during parsing
154 };
155
156 // location as key
157 std::map<std::string, schema_file> files_;
158
159 schema_file &get_or_create_file(const std::string &loc)
160 {
161 auto file = files_.lower_bound(loc);
162 if (file != files_.end() && !(files_.key_comp()(loc, file->first)))
163 return file->second;
164 else
165 return files_.insert(file, {loc, {}})->second;
166 }
167
168public:
170 format_checker &&format,
171 content_checker &&content)
172
173 : loader_(std::move(loader)),
174 format_check_(std::move(format)),
175 content_check_(std::move(content))
176 {
177 }
178
181
182 void insert(const json_uri &uri, const std::shared_ptr<schema> &s)
183 {
184 auto &file = get_or_create_file(uri.location());
185 auto sch = file.schemas.lower_bound(uri.fragment());
186 if (sch != file.schemas.end() && !(file.schemas.key_comp()(uri.fragment(), sch->first))) {
187 throw std::invalid_argument("schema with " + uri.to_string() + " already inserted");
188 return;
189 }
190
191 file.schemas.insert({uri.fragment(), s});
192
193 // was someone referencing this newly inserted schema?
194 auto unresolved = file.unresolved.find(uri.fragment());
195 if (unresolved != file.unresolved.end()) {
196 unresolved->second->set_target(s);
197 file.unresolved.erase(unresolved);
198 }
199 }
200
201 void insert_unknown_keyword(const json_uri &uri, const std::string &key, json &value)
202 {
203 auto &file = get_or_create_file(uri.location());
204 auto new_uri = uri.append(key);
205 auto fragment = new_uri.pointer();
206
207 // is there a reference looking for this unknown-keyword, which is thus no longer a unknown keyword but a schema
208 auto unresolved = file.unresolved.find(fragment.to_string());
209 if (unresolved != file.unresolved.end())
210 schema::make(value, this, {}, {{new_uri}});
211 else { // no, nothing ref'd it, keep for later
212
213 // need to create an object for each reference-token in the
214 // JSON-Pointer When not existing, a stringified integer reference
215 // token (e.g. "123") in the middle of the pointer will be
216 // interpreted a an array-index and an array will be created.
217
218 // json_pointer's reference_tokens is private - get them
219 std::deque<std::string> ref_tokens;
220 auto uri_pointer = uri.pointer();
221 while (!uri_pointer.empty()) {
222 ref_tokens.push_front(uri_pointer.back());
223 uri_pointer.pop_back();
224 }
225
226 // for each token create an object, if not already existing
227 auto unk_kw = &file.unknown_keywords;
228 for (auto &rt : ref_tokens) {
229 // create a json_pointer from rt as rt can be an stringified integer doing find on an array won't work
230 json::json_pointer rt_ptr{"/" + rt};
231 if (unk_kw->contains(rt_ptr) == false)
232 (*unk_kw)[rt] = json::object();
233 unk_kw = &(*unk_kw)[rt_ptr];
234 }
235 (*unk_kw)[key] = value;
236 }
237
238 // recursively add possible subschemas of unknown keywords
239 if (value.type() == json::value_t::object)
240 for (auto &subsch : value.items())
241 insert_unknown_keyword(new_uri, subsch.key(), subsch.value());
242 }
243
244 std::shared_ptr<schema> get_or_create_ref(const json_uri &uri)
245 {
246 auto &file = get_or_create_file(uri.location());
247
248 // existing schema
249 auto sch = file.schemas.find(uri.fragment());
250 if (sch != file.schemas.end())
251 return sch->second;
252
253 // referencing an unknown keyword, turn it into schema
254 //
255 // an unknown keyword can only be referenced by a json-pointer,
256 // not by a plain name fragment
257 if (!uri.pointer().to_string().empty()) {
258 bool contains_pointer = file.unknown_keywords.contains(uri.pointer());
259 if (contains_pointer) {
260 auto &subschema = file.unknown_keywords.at(uri.pointer());
261 auto s = schema::make(subschema, this, {}, {{uri}});
262 if (s) { // if schema is valid (non-null)
263 file.unknown_keywords.erase(uri.fragment());
264 return s;
265 }
266 }
267 }
268
269 // get or create a schema_ref
270 auto r = file.unresolved.lower_bound(uri.fragment());
271 if (r != file.unresolved.end() && !(file.unresolved.key_comp()(uri.fragment(), r->first))) {
272 return r->second; // unresolved, already seen previously - use existing reference
273 } else {
274 return file.unresolved.insert(r,
275 {uri.fragment(), std::make_shared<schema_ref>(uri.to_string(), this)})
276 ->second; // unresolved, create reference
277 }
278 }
279
280 void set_root_schema(json sch)
281 {
282 files_.clear();
283 root_ = schema::make(sch, this, {}, {{"#"}});
284
285 // load all files which have not yet been loaded
286 do {
287 bool new_schema_loaded = false;
288
289 // files_ is modified during parsing, iterators are invalidated
290 std::vector<std::string> locations;
291 for (auto &file : files_)
292 locations.push_back(file.first);
293
294 for (auto &loc : locations) {
295 if (files_[loc].schemas.size() == 0) { // nothing has been loaded for this file
296 if (loader_) {
297 json loaded_schema;
298
299 loader_(loc, loaded_schema);
300
301 schema::make(loaded_schema, this, {}, {{loc}});
302 new_schema_loaded = true;
303 } else {
304 throw std::invalid_argument("external schema reference '" + loc + "' needs loading, but no loader callback given");
305 }
306 }
307 }
308
309 if (!new_schema_loaded) // if no new schema loaded, no need to try again
310 break;
311 } while (1);
312
313 for (const auto &file : files_) {
314 if (file.second.unresolved.size() != 0) {
315 // Build a representation of the undefined
316 // references as a list of comma-separated strings.
317 auto n_urefs = file.second.unresolved.size();
318 std::string urefs = "[";
319
320 decltype(n_urefs) counter = 0;
321 for (const auto &p : file.second.unresolved) {
322 urefs += p.first;
323
324 if (counter != n_urefs - 1u) {
325 urefs += ", ";
326 }
327
328 ++counter;
329 }
330
331 urefs += "]";
332
333 throw std::invalid_argument("after all files have been parsed, '" +
334 (file.first == "" ? "<root>" : file.first) +
335 "' has still the following undefined references: " + urefs);
336 }
337 }
338 }
339
340 void validate(const json::json_pointer &ptr,
341 const json &instance,
342 json_patch &patch,
343 error_handler &e,
344 const json_uri &initial) const
345 {
346 if (!root_) {
347 e.error(ptr, "", "no root schema has yet been set for validating an instance");
348 return;
349 }
350
351 auto file_entry = files_.find(initial.location());
352 if (file_entry == files_.end()) {
353 e.error(ptr, "", "no file found serving requested root-URI. " + initial.location());
354 return;
355 }
356
357 auto &file = file_entry->second;
358 auto sch = file.schemas.find(initial.fragment());
359 if (sch == file.schemas.end()) {
360 e.error(ptr, "", "no schema find for request initial URI: " + initial.to_string());
361 return;
362 }
363
364 sch->second->validate(ptr, instance, patch, e);
365 }
366};
367
368} // namespace json_schema
369} // namespace nlohmann
370
371namespace
372{
373
375{
376public:
377 bool error_{false};
378 json::json_pointer ptr_;
380 std::string message_;
381
382 void error(const json::json_pointer &ptr, const json &instance, const std::string &message) override
383 {
384 if (*this)
385 return;
386 error_ = true;
387 ptr_ = ptr;
388 instance_ = instance;
389 message_ = message;
390 }
391
392 operator bool() const { return error_; }
393};
394
395class logical_not : public schema
396{
397 std::shared_ptr<schema> subschema_;
398
399 void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const final
400 {
402 subschema_->validate(ptr, instance, patch, esub);
403
404 if (!esub)
405 e.error(ptr, instance, "the subschema has succeeded, but it is required to not validate");
406 }
407
408 const json &default_value(const json::json_pointer &ptr, const json &instance, error_handler &e) const override
409 {
410 return subschema_->default_value(ptr, instance, e);
411 }
412
413public:
414 logical_not(json &sch,
415 root_schema *root,
416 const std::vector<nlohmann::json_uri> &uris)
417 : schema(root)
418 {
419 subschema_ = schema::make(sch, root, {"not"}, uris);
420 }
421};
422
428
430{
431public:
432 struct error_entry {
433 json::json_pointer ptr_;
435 std::string message_;
436 };
437
439
440 void error(const json::json_pointer &ptr, const json &instance, const std::string &message) override
441 {
442 error_entry_list_.push_back(error_entry{ptr, instance, message});
443 }
444
445 void propagate(error_handler &e, const std::string &prefix) const
446 {
447 for (const error_entry &entry : error_entry_list_)
448 e.error(entry.ptr_, entry.instance_, prefix + entry.message_);
449 }
450
451 operator bool() const { return !error_entry_list_.empty(); }
452};
453
454template <enum logical_combination_types combine_logic>
456{
457 std::vector<std::shared_ptr<schema>> subschemata_;
458
459 void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const final
460 {
461 size_t count = 0;
463
464 for (std::size_t index = 0; index < subschemata_.size(); ++index) {
465 const std::shared_ptr<schema> &s = subschemata_[index];
467 auto oldPatchSize = patch.get_json().size();
468 s->validate(ptr, instance, patch, esub);
469 if (!esub)
470 count++;
471 else {
472 patch.get_json().get_ref<nlohmann::json::array_t &>().resize(oldPatchSize);
473 esub.propagate(error_summary, "case#" + std::to_string(index) + "] ");
474 }
475
476 if (is_validate_complete(instance, ptr, e, esub, count, index))
477 return;
478 }
479
480 if (count == 0) {
481 e.error(ptr, instance, "no subschema has succeeded, but one of them is required to validate. Type: " + key + ", number of failed subschemas: " + std::to_string(subschemata_.size()));
482 error_summary.propagate(e, "[combination: " + key + " / ");
483 }
484 }
485
486 // specialized for each of the logical_combination_types
487 static const std::string key;
488 static bool is_validate_complete(const json &, const json::json_pointer &, error_handler &, const logical_combination_error_handler &, size_t, size_t);
489
490public:
492 root_schema *root,
493 const std::vector<nlohmann::json_uri> &uris)
494 : schema(root)
495 {
496 size_t c = 0;
497 for (auto &subschema : sch)
498 subschemata_.push_back(schema::make(subschema, root, {key, std::to_string(c++)}, uris));
499
500 // value of allOf, anyOf, and oneOf "MUST be a non-empty array"
501 // TODO error/throw? when subschemata_.empty()
502 }
503};
504
505template <>
506const std::string logical_combination<allOf>::key = "allOf";
507template <>
508const std::string logical_combination<anyOf>::key = "anyOf";
509template <>
510const std::string logical_combination<oneOf>::key = "oneOf";
511
512template <>
513bool logical_combination<allOf>::is_validate_complete(const json &, const json::json_pointer &, error_handler &e, const logical_combination_error_handler &esub, size_t, size_t current_schema_index)
514{
515 if (esub) {
516 e.error(esub.error_entry_list_.front().ptr_, esub.error_entry_list_.front().instance_, "at least one subschema has failed, but all of them are required to validate - " + esub.error_entry_list_.front().message_);
517 esub.propagate(e, "[combination: allOf / case#" + std::to_string(current_schema_index) + "] ");
518 }
519 return esub;
520}
521
522template <>
523bool logical_combination<anyOf>::is_validate_complete(const json &, const json::json_pointer &, error_handler &, const logical_combination_error_handler &, size_t count, size_t)
524{
525 return count == 1;
526}
527
528template <>
529bool logical_combination<oneOf>::is_validate_complete(const json &instance, const json::json_pointer &ptr, error_handler &e, const logical_combination_error_handler &, size_t count, size_t)
530{
531 if (count > 1)
532 e.error(ptr, instance, "more than one subschema has succeeded, but exactly one of them is required to validate");
533 return count > 1;
534}
535
536class type_schema : public schema
537{
538 std::vector<std::shared_ptr<schema>> type_;
539 std::pair<bool, json> enum_, const_;
540 std::vector<std::shared_ptr<schema>> logic_;
541
542 static std::shared_ptr<schema> make(json &schema,
543 json::value_t type,
544 root_schema *,
545 const std::vector<nlohmann::json_uri> &,
546 std::set<std::string> &);
547
548 std::shared_ptr<schema> if_, then_, else_;
549
550 void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const override final
551 {
552 // depending on the type of instance run the type specific validator - if present
553 auto type = type_[static_cast<uint8_t>(instance.type())];
554
555 if (type)
556 type->validate(ptr, instance, patch, e);
557 else
558 e.error(ptr, instance, "unexpected instance type");
559
560 if (enum_.first) {
561 bool seen_in_enum = false;
562 for (auto &v : enum_.second)
563 if (instance == v) {
564 seen_in_enum = true;
565 break;
566 }
567
568 if (!seen_in_enum)
569 e.error(ptr, instance, "instance not found in required enum");
570 }
571
572 if (const_.first &&
573 const_.second != instance)
574 e.error(ptr, instance, "instance not const");
575
576 for (auto l : logic_)
577 l->validate(ptr, instance, patch, e);
578
579 if (if_) {
581
582 if_->validate(ptr, instance, patch, err);
583 if (!err) {
584 if (then_)
585 then_->validate(ptr, instance, patch, e);
586 } else {
587 if (else_)
588 else_->validate(ptr, instance, patch, e);
589 }
590 }
591 if (instance.is_null()) {
592 patch.add(nlohmann::json::json_pointer{}, default_value_);
593 }
594 }
595
596protected:
597 virtual std::shared_ptr<schema> make_for_default_(
598 std::shared_ptr<::schema> & /* sch */,
599 root_schema * /* root */,
600 std::vector<nlohmann::json_uri> & /* uris */,
601 nlohmann::json &default_value) const override
602 {
603 auto result = std::make_shared<type_schema>(*this);
604 result->set_default_value(default_value);
605 return result;
606 };
607
608public:
609 type_schema(json &sch,
610 root_schema *root,
611 const std::vector<nlohmann::json_uri> &uris)
612 : schema(root), type_(static_cast<uint8_t>(json::value_t::discarded) + 1)
613 {
614 // association between JSON-schema-type and NLohmann-types
615 static const std::vector<std::pair<std::string, json::value_t>> schema_types = {
616 {"null", json::value_t::null},
617 {"object", json::value_t::object},
618 {"array", json::value_t::array},
619 {"string", json::value_t::string},
620 {"boolean", json::value_t::boolean},
621 {"integer", json::value_t::number_integer},
622 {"number", json::value_t::number_float},
623 };
624
625 std::set<std::string> known_keywords;
626
627 auto attr = sch.find("type");
628 if (attr == sch.end()) // no type field means all sub-types possible
629 for (auto &t : schema_types)
630 type_[static_cast<uint8_t>(t.second)] = type_schema::make(sch, t.second, root, uris, known_keywords);
631 else {
632 switch (attr.value().type()) { // "type": "type"
633
634 case json::value_t::string: {
635 auto schema_type = attr.value().get<std::string>();
636 for (auto &t : schema_types)
637 if (t.first == schema_type)
638 type_[static_cast<uint8_t>(t.second)] = type_schema::make(sch, t.second, root, uris, known_keywords);
639 } break;
640
641 case json::value_t::array: // "type": ["type1", "type2"]
642 for (auto &array_value : attr.value()) {
643 auto schema_type = array_value.get<std::string>();
644 for (auto &t : schema_types)
645 if (t.first == schema_type)
646 type_[static_cast<uint8_t>(t.second)] = type_schema::make(sch, t.second, root, uris, known_keywords);
647 }
648 break;
649
650 default:
651 break;
652 }
653
654 sch.erase(attr);
655 }
656
657 attr = sch.find("default");
658 if (attr != sch.end()) {
659 set_default_value(attr.value());
660 sch.erase(attr);
661 }
662
663 for (auto &key : known_keywords)
664 sch.erase(key);
665
666 // with nlohmann::json float instance (but number in schema-definition) can be seen as unsigned or integer -
667 // reuse the number-validator for integer values as well, if they have not been specified explicitly
668 if (type_[static_cast<uint8_t>(json::value_t::number_float)] && !type_[static_cast<uint8_t>(json::value_t::number_integer)])
669 type_[static_cast<uint8_t>(json::value_t::number_integer)] = type_[static_cast<uint8_t>(json::value_t::number_float)];
670
671 // #54: JSON-schema does not differentiate between unsigned and signed integer - nlohmann::json does
672 // we stick with JSON-schema: use the integer-validator if instance-value is unsigned
673 type_[static_cast<uint8_t>(json::value_t::number_unsigned)] = type_[static_cast<uint8_t>(json::value_t::number_integer)];
674
675 // special for binary types
676 if (type_[static_cast<uint8_t>(json::value_t::string)]) {
677 type_[static_cast<uint8_t>(json::value_t::binary)] = type_[static_cast<uint8_t>(json::value_t::string)];
678 }
679
680 attr = sch.find("enum");
681 if (attr != sch.end()) {
682 enum_ = {true, attr.value()};
683 sch.erase(attr);
684 }
685
686 attr = sch.find("const");
687 if (attr != sch.end()) {
688 const_ = {true, attr.value()};
689 sch.erase(attr);
690 }
691
692 attr = sch.find("not");
693 if (attr != sch.end()) {
694 logic_.push_back(std::make_shared<logical_not>(attr.value(), root, uris));
695 sch.erase(attr);
696 }
697
698 attr = sch.find("allOf");
699 if (attr != sch.end()) {
700 logic_.push_back(std::make_shared<logical_combination<allOf>>(attr.value(), root, uris));
701 sch.erase(attr);
702 }
703
704 attr = sch.find("anyOf");
705 if (attr != sch.end()) {
706 logic_.push_back(std::make_shared<logical_combination<anyOf>>(attr.value(), root, uris));
707 sch.erase(attr);
708 }
709
710 attr = sch.find("oneOf");
711 if (attr != sch.end()) {
712 logic_.push_back(std::make_shared<logical_combination<oneOf>>(attr.value(), root, uris));
713 sch.erase(attr);
714 }
715
716 attr = sch.find("if");
717 if (attr != sch.end()) {
718 auto attr_then = sch.find("then");
719 auto attr_else = sch.find("else");
720
721 if (attr_then != sch.end() || attr_else != sch.end()) {
722 if_ = schema::make(attr.value(), root, {"if"}, uris);
723
724 if (attr_then != sch.end()) {
725 then_ = schema::make(attr_then.value(), root, {"then"}, uris);
726 sch.erase(attr_then);
727 }
728
729 if (attr_else != sch.end()) {
730 else_ = schema::make(attr_else.value(), root, {"else"}, uris);
731 sch.erase(attr_else);
732 }
733 }
734 sch.erase(attr);
735 }
736 }
737};
738
739class string : public schema
740{
741 std::pair<bool, size_t> maxLength_{false, 0};
742 std::pair<bool, size_t> minLength_{false, 0};
743
744#ifndef NO_STD_REGEX
745 std::pair<bool, REGEX_NAMESPACE::regex> pattern_{false, REGEX_NAMESPACE::regex()};
746 std::string patternString_;
747#endif
748
749 std::pair<bool, std::string> format_;
750 std::tuple<bool, std::string, std::string> content_{false, "", ""};
751
752 std::size_t utf8_length(const std::string &s) const
753 {
754 size_t len = 0;
755 for (auto c : s)
756 if ((c & 0xc0) != 0x80)
757 len++;
758 return len;
759 }
760
761 void validate(const json::json_pointer &ptr, const json &instance, json_patch &, error_handler &e) const override
762 {
763 if (minLength_.first) {
764 if (utf8_length(instance.get<std::string>()) < minLength_.second) {
765 std::ostringstream s;
766 s << "instance is too short as per minLength:" << minLength_.second;
767 e.error(ptr, instance, s.str());
768 }
769 }
770
771 if (maxLength_.first) {
772 if (utf8_length(instance.get<std::string>()) > maxLength_.second) {
773 std::ostringstream s;
774 s << "instance is too long as per maxLength: " << maxLength_.second;
775 e.error(ptr, instance, s.str());
776 }
777 }
778
779 if (std::get<0>(content_)) {
780 if (root_->content_check() == nullptr)
781 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_) + "'");
782 else {
783 try {
784 root_->content_check()(std::get<1>(content_), std::get<2>(content_), instance);
785 } catch (const std::exception &ex) {
786 e.error(ptr, instance, std::string("content-checking failed: ") + ex.what());
787 }
788 }
789 } else if (instance.type() == json::value_t::binary) {
790 e.error(ptr, instance, "expected string, but get binary data");
791 }
792
793 if (instance.type() != json::value_t::string) {
794 return; // next checks only for strings
795 }
796
797#ifndef NO_STD_REGEX
798 if (pattern_.first &&
799 !REGEX_NAMESPACE::regex_search(instance.get<std::string>(), pattern_.second))
800 e.error(ptr, instance, "instance does not match regex pattern: " + patternString_);
801#endif
802
803 if (format_.first) {
804 if (root_->format_check() == nullptr)
805 e.error(ptr, instance, std::string("a format checker was not provided but a format keyword for this string is present: ") + format_.second);
806 else {
807 try {
808 root_->format_check()(format_.second, instance.get<std::string>());
809 } catch (const std::exception &ex) {
810 e.error(ptr, instance, std::string("format-checking failed: ") + ex.what());
811 }
812 }
813 }
814 }
815
816public:
817 string(json &sch, root_schema *root)
818 : schema(root)
819 {
820 auto attr = sch.find("maxLength");
821 if (attr != sch.end()) {
822 maxLength_ = {true, attr.value().get<size_t>()};
823 sch.erase(attr);
824 }
825
826 attr = sch.find("minLength");
827 if (attr != sch.end()) {
828 minLength_ = {true, attr.value().get<size_t>()};
829 sch.erase(attr);
830 }
831
832 attr = sch.find("contentEncoding");
833 if (attr != sch.end()) {
834 std::get<0>(content_) = true;
835 std::get<1>(content_) = attr.value().get<std::string>();
836
837 // special case for nlohmann::json-binary-types
838 //
839 // https://github.com/pboettch/json-schema-validator/pull/114
840 //
841 // We cannot use explicitly in a schema: {"type": "binary"} or
842 // "type": ["binary", "number"] we have to be implicit. For a
843 // schema where "contentEncoding" is set to "binary", an instance
844 // of type json::value_t::binary is accepted. If a
845 // contentEncoding-callback has to be provided and is called
846 // accordingly. For encoding=binary, no other type validations are done
847
848 sch.erase(attr);
849 }
850
851 attr = sch.find("contentMediaType");
852 if (attr != sch.end()) {
853 std::get<0>(content_) = true;
854 std::get<2>(content_) = attr.value().get<std::string>();
855
856 sch.erase(attr);
857 }
858
859 if (std::get<0>(content_) == true && root_->content_check() == nullptr) {
860 throw std::invalid_argument{"schema contains contentEncoding/contentMediaType but content checker was not set"};
861 }
862
863#ifndef NO_STD_REGEX
864 attr = sch.find("pattern");
865 if (attr != sch.end()) {
866 patternString_ = attr.value().get<std::string>();
867 pattern_ = {true, REGEX_NAMESPACE::regex(attr.value().get<std::string>(),
868 REGEX_NAMESPACE::regex::ECMAScript)};
869 sch.erase(attr);
870 }
871#endif
872
873 attr = sch.find("format");
874 if (attr != sch.end()) {
875 if (root_->format_check() == nullptr)
876 throw std::invalid_argument{"a format checker was not provided but a format keyword for this string is present: " + format_.second};
877
878 format_ = {true, attr.value().get<std::string>()};
879 sch.erase(attr);
880 }
881 }
882};
883
884template <typename T>
885class numeric : public schema
886{
887 std::pair<bool, T> maximum_{false, 0};
888 std::pair<bool, T> minimum_{false, 0};
889
890 bool exclusiveMaximum_ = false;
891 bool exclusiveMinimum_ = false;
892
893 std::pair<bool, json::number_float_t> multipleOf_{false, 0};
894
895 // multipleOf - if the remainder of the division is 0 -> OK
896 bool violates_multiple_of(T x) const
897 {
898 double res = std::remainder(x, multipleOf_.second);
899 double multiple = std::fabs(static_cast<double>(x) / multipleOf_.second);
900 if (multiple > 1) {
901 res = res / multiple;
902 }
903 double eps = std::nextafter(x, 0) - static_cast<double>(x);
904
905 return std::fabs(res) > std::fabs(eps);
906 }
907
908 void validate(const json::json_pointer &ptr, const json &instance, json_patch &, error_handler &e) const override
909 {
910 T value = instance; // conversion of json to value_type
911
912 std::ostringstream oss;
913
914 if (multipleOf_.first && value != 0) // zero is multiple of everything
915 if (violates_multiple_of(value))
916 oss << "instance is not a multiple of " << json(multipleOf_.second);
917
918 if (maximum_.first) {
919 if (exclusiveMaximum_ && value >= maximum_.second)
920 oss << "instance exceeds or equals maximum of " << json(maximum_.second);
921 else if (value > maximum_.second)
922 oss << "instance exceeds maximum of " << json(maximum_.second);
923 }
924
925 if (minimum_.first) {
926 if (exclusiveMinimum_ && value <= minimum_.second)
927 oss << "instance is below or equals minimum of " << json(minimum_.second);
928 else if (value < minimum_.second)
929 oss << "instance is below minimum of " << json(minimum_.second);
930 }
931
932 oss.seekp(0, std::ios::end);
933 auto size = oss.tellp();
934 if (size != 0) {
935 oss.seekp(0, std::ios::beg);
936 e.error(ptr, instance, oss.str());
937 }
938 }
939
940public:
941 numeric(const json &sch, root_schema *root, std::set<std::string> &kw)
942 : schema(root)
943 {
944 auto attr = sch.find("maximum");
945 if (attr != sch.end()) {
946 maximum_ = {true, attr.value().get<T>()};
947 kw.insert("maximum");
948 }
949
950 attr = sch.find("minimum");
951 if (attr != sch.end()) {
952 minimum_ = {true, attr.value().get<T>()};
953 kw.insert("minimum");
954 }
955
956 attr = sch.find("exclusiveMaximum");
957 if (attr != sch.end()) {
958 exclusiveMaximum_ = true;
959 maximum_ = {true, attr.value().get<T>()};
960 kw.insert("exclusiveMaximum");
961 }
962
963 attr = sch.find("exclusiveMinimum");
964 if (attr != sch.end()) {
965 exclusiveMinimum_ = true;
966 minimum_ = {true, attr.value().get<T>()};
967 kw.insert("exclusiveMinimum");
968 }
969
970 attr = sch.find("multipleOf");
971 if (attr != sch.end()) {
972 multipleOf_ = {true, attr.value().get<json::number_float_t>()};
973 kw.insert("multipleOf");
974 }
975 }
976};
977
978class null : public schema
979{
980 void validate(const json::json_pointer &ptr, const json &instance, json_patch &, error_handler &e) const override
981 {
982 if (!instance.is_null())
983 e.error(ptr, instance, "expected to be null");
984 }
985
986public:
987 null(json &, root_schema *root)
988 : schema(root) {}
989};
990
991class boolean_type : public schema
992{
993 void validate(const json::json_pointer &, const json &, json_patch &, error_handler &) const override {}
994
995public:
997 : schema(root) {}
998};
999
1000class boolean : public schema
1001{
1002 bool true_;
1003 void validate(const json::json_pointer &ptr, const json &instance, json_patch &, error_handler &e) const override
1004 {
1005 if (!true_) { // false schema
1006 // empty array
1007 // switch (instance.type()) {
1008 // case json::value_t::array:
1009 // if (instance.size() != 0) // valid false-schema
1010 // e.error(ptr, instance, "false-schema required empty array");
1011 // return;
1012 //}
1013
1014 e.error(ptr, instance, "instance invalid as per false-schema");
1015 }
1016 }
1017
1018public:
1019 boolean(json &sch, root_schema *root)
1020 : schema(root), true_(sch) {}
1021};
1022
1023class required : public schema
1024{
1025 const std::vector<std::string> required_;
1026
1027 void validate(const json::json_pointer &ptr, const json &instance, json_patch &, error_handler &e) const override final
1028 {
1029 for (auto &r : required_)
1030 if (instance.find(r) == instance.end())
1031 e.error(ptr, instance, "required property '" + r + "' not found in object as a dependency");
1032 }
1033
1034public:
1035 required(const std::vector<std::string> &r, root_schema *root)
1036 : schema(root), required_(r) {}
1037};
1038
1039class object : public schema
1040{
1041 std::pair<bool, size_t> maxProperties_{false, 0};
1042 std::pair<bool, size_t> minProperties_{false, 0};
1043 std::vector<std::string> required_;
1044
1045 std::map<std::string, std::shared_ptr<schema>> properties_;
1046#ifndef NO_STD_REGEX
1047 std::vector<std::pair<REGEX_NAMESPACE::regex, std::shared_ptr<schema>>> patternProperties_;
1048#endif
1050
1051 std::map<std::string, std::shared_ptr<schema>> dependencies_;
1052
1053 std::shared_ptr<schema> propertyNames_;
1054
1055 void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const override
1056 {
1057 if (maxProperties_.first && instance.size() > maxProperties_.second)
1058 e.error(ptr, instance, "too many properties");
1059
1060 if (minProperties_.first && instance.size() < minProperties_.second)
1061 e.error(ptr, instance, "too few properties");
1062
1063 for (auto &r : required_)
1064 if (instance.find(r) == instance.end())
1065 e.error(ptr, instance, "required property '" + r + "' not found in object");
1066
1067 // for each property in instance
1068 for (auto &p : instance.items()) {
1069 if (propertyNames_)
1070 propertyNames_->validate(ptr, p.key(), patch, e);
1071
1072 bool a_prop_or_pattern_matched = false;
1073 auto schema_p = properties_.find(p.key());
1074 // check if it is in "properties"
1075 if (schema_p != properties_.end()) {
1076 a_prop_or_pattern_matched = true;
1077 schema_p->second->validate(ptr / p.key(), p.value(), patch, e);
1078 }
1079
1080#ifndef NO_STD_REGEX
1081 // check all matching patternProperties
1082 for (auto &schema_pp : patternProperties_)
1083 if (REGEX_NAMESPACE::regex_search(p.key(), schema_pp.first)) {
1084 a_prop_or_pattern_matched = true;
1085 schema_pp.second->validate(ptr / p.key(), p.value(), patch, e);
1086 }
1087#endif
1088
1089 // check additionalProperties as a last resort
1090 if (!a_prop_or_pattern_matched && additionalProperties_) {
1091 first_error_handler additional_prop_err;
1092 additionalProperties_->validate(ptr / p.key(), p.value(), patch, additional_prop_err);
1093 if (additional_prop_err)
1094 e.error(ptr, instance, "validation failed for additional property '" + p.key() + "': " + additional_prop_err.message_);
1095 }
1096 }
1097
1098 // reverse search
1099 for (auto const &prop : properties_) {
1100 const auto finding = instance.find(prop.first);
1101 if (instance.end() == finding) { // if the prop is not in the instance
1102 const auto &default_value = prop.second->default_value(ptr, instance, e);
1103 if (!default_value.is_null()) { // if default value is available
1104 patch.add((ptr / prop.first), default_value);
1105 }
1106 }
1107 }
1108
1109 for (auto &dep : dependencies_) {
1110 auto prop = instance.find(dep.first);
1111 if (prop != instance.end()) // if dependency-property is present in instance
1112 dep.second->validate(ptr / dep.first, instance, patch, e); // validate
1113 }
1114 }
1115
1116public:
1117 object(json &sch,
1118 root_schema *root,
1119 const std::vector<nlohmann::json_uri> &uris)
1120 : schema(root)
1121 {
1122 auto attr = sch.find("maxProperties");
1123 if (attr != sch.end()) {
1124 maxProperties_ = {true, attr.value().get<size_t>()};
1125 sch.erase(attr);
1126 }
1127
1128 attr = sch.find("minProperties");
1129 if (attr != sch.end()) {
1130 minProperties_ = {true, attr.value().get<size_t>()};
1131 sch.erase(attr);
1132 }
1133
1134 attr = sch.find("required");
1135 if (attr != sch.end()) {
1136 required_ = attr.value().get<std::vector<std::string>>();
1137 sch.erase(attr);
1138 }
1139
1140 attr = sch.find("properties");
1141 if (attr != sch.end()) {
1142 for (auto prop : attr.value().items())
1143 properties_.insert(
1144 std::make_pair(
1145 prop.key(),
1146 schema::make(prop.value(), root, {"properties", prop.key()}, uris)));
1147 sch.erase(attr);
1148 }
1149
1150#ifndef NO_STD_REGEX
1151 attr = sch.find("patternProperties");
1152 if (attr != sch.end()) {
1153 for (auto prop : attr.value().items())
1154 patternProperties_.push_back(
1155 std::make_pair(
1156 REGEX_NAMESPACE::regex(prop.key(), REGEX_NAMESPACE::regex::ECMAScript),
1157 schema::make(prop.value(), root, {prop.key()}, uris)));
1158 sch.erase(attr);
1159 }
1160#endif
1161
1162 attr = sch.find("additionalProperties");
1163 if (attr != sch.end()) {
1164 additionalProperties_ = schema::make(attr.value(), root, {"additionalProperties"}, uris);
1165 sch.erase(attr);
1166 }
1167
1168 attr = sch.find("dependencies");
1169 if (attr != sch.end()) {
1170 for (auto &dep : attr.value().items())
1171 switch (dep.value().type()) {
1172 case json::value_t::array:
1173 dependencies_.emplace(dep.key(),
1174 std::make_shared<required>(
1175 dep.value().get<std::vector<std::string>>(), root));
1176 break;
1177
1178 default:
1179 dependencies_.emplace(dep.key(),
1180 schema::make(dep.value(), root, {"dependencies", dep.key()}, uris));
1181 break;
1182 }
1183 sch.erase(attr);
1184 }
1185
1186 attr = sch.find("propertyNames");
1187 if (attr != sch.end()) {
1188 propertyNames_ = schema::make(attr.value(), root, {"propertyNames"}, uris);
1189 sch.erase(attr);
1190 }
1191
1192 attr = sch.find("default");
1193 if (attr != sch.end()) {
1195 }
1196 }
1197};
1198
1199class array : public schema
1200{
1201 std::pair<bool, size_t> maxItems_{false, 0};
1202 std::pair<bool, size_t> minItems_{false, 0};
1203 bool uniqueItems_ = false;
1204
1205 std::shared_ptr<schema> items_schema_;
1206
1207 std::vector<std::shared_ptr<schema>> items_;
1208 std::shared_ptr<schema> additionalItems_;
1209
1210 std::shared_ptr<schema> contains_;
1211
1212 void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const override
1213 {
1214 if (maxItems_.first && instance.size() > maxItems_.second)
1215 e.error(ptr, instance, "array has too many items");
1216
1217 if (minItems_.first && instance.size() < minItems_.second)
1218 e.error(ptr, instance, "array has too few items");
1219
1220 if (uniqueItems_) {
1221 for (auto it = instance.cbegin(); it != instance.cend(); ++it) {
1222 auto v = std::find(it + 1, instance.end(), *it);
1223 if (v != instance.end())
1224 e.error(ptr, instance, "items have to be unique for this array");
1225 }
1226 }
1227
1228 size_t index = 0;
1229 if (items_schema_)
1230 for (auto &i : instance) {
1231 items_schema_->validate(ptr / index, i, patch, e);
1232 index++;
1233 }
1234 else {
1235 auto item = items_.cbegin();
1236 for (auto &i : instance) {
1237 std::shared_ptr<schema> item_validator;
1238 if (item == items_.cend())
1239 item_validator = additionalItems_;
1240 else {
1241 item_validator = *item;
1242 item++;
1243 }
1244
1245 if (!item_validator)
1246 break;
1247
1248 item_validator->validate(ptr / index, i, patch, e);
1249 }
1250 }
1251
1252 if (contains_) {
1253 bool contained = false;
1254 for (auto &item : instance) {
1255 first_error_handler local_e;
1256 contains_->validate(ptr, item, patch, local_e);
1257 if (!local_e) {
1258 contained = true;
1259 break;
1260 }
1261 }
1262 if (!contained)
1263 e.error(ptr, instance, "array does not contain required element as per 'contains'");
1264 }
1265 }
1266
1267public:
1268 array(json &sch, root_schema *root, const std::vector<nlohmann::json_uri> &uris)
1269 : schema(root)
1270 {
1271 auto attr = sch.find("maxItems");
1272 if (attr != sch.end()) {
1273 maxItems_ = {true, attr.value().get<size_t>()};
1274 sch.erase(attr);
1275 }
1276
1277 attr = sch.find("minItems");
1278 if (attr != sch.end()) {
1279 minItems_ = {true, attr.value().get<size_t>()};
1280 sch.erase(attr);
1281 }
1282
1283 attr = sch.find("uniqueItems");
1284 if (attr != sch.end()) {
1285 uniqueItems_ = attr.value().get<bool>();
1286 sch.erase(attr);
1287 }
1288
1289 attr = sch.find("items");
1290 if (attr != sch.end()) {
1291
1292 if (attr.value().type() == json::value_t::array) {
1293 size_t c = 0;
1294 for (auto &subsch : attr.value())
1295 items_.push_back(schema::make(subsch, root, {"items", std::to_string(c++)}, uris));
1296
1297 auto attr_add = sch.find("additionalItems");
1298 if (attr_add != sch.end()) {
1299 additionalItems_ = schema::make(attr_add.value(), root, {"additionalItems"}, uris);
1300 sch.erase(attr_add);
1301 }
1302
1303 } else if (attr.value().type() == json::value_t::object ||
1304 attr.value().type() == json::value_t::boolean)
1305 items_schema_ = schema::make(attr.value(), root, {"items"}, uris);
1306
1307 sch.erase(attr);
1308 }
1309
1310 attr = sch.find("contains");
1311 if (attr != sch.end()) {
1312 contains_ = schema::make(attr.value(), root, {"contains"}, uris);
1313 sch.erase(attr);
1314 }
1315 }
1316};
1317
1318std::shared_ptr<schema> type_schema::make(json &schema,
1319 json::value_t type,
1320 root_schema *root,
1321 const std::vector<nlohmann::json_uri> &uris,
1322 std::set<std::string> &kw)
1323{
1324 switch (type) {
1325 case json::value_t::null:
1326 return std::make_shared<null>(schema, root);
1327
1328 case json::value_t::number_unsigned:
1329 case json::value_t::number_integer:
1330 return std::make_shared<numeric<json::number_integer_t>>(schema, root, kw);
1331 case json::value_t::number_float:
1332 return std::make_shared<numeric<json::number_float_t>>(schema, root, kw);
1333 case json::value_t::string:
1334 return std::make_shared<string>(schema, root);
1335 case json::value_t::boolean:
1336 return std::make_shared<boolean_type>(schema, root);
1337 case json::value_t::object:
1338 return std::make_shared<object>(schema, root, uris);
1339 case json::value_t::array:
1340 return std::make_shared<array>(schema, root, uris);
1341
1342 case json::value_t::discarded: // not a real type - silence please
1343 break;
1344
1345 case json::value_t::binary:
1346 break;
1347 }
1348 return nullptr;
1349}
1350} // namespace
1351
1352namespace
1353{
1354
1355std::shared_ptr<schema> schema::make(json &schema,
1356 root_schema *root,
1357 const std::vector<std::string> &keys,
1358 std::vector<nlohmann::json_uri> uris)
1359{
1360 // remove URIs which contain plain name identifiers, as sub-schemas cannot be referenced
1361 for (auto uri = uris.begin(); uri != uris.end();)
1362 if (uri->identifier() != "")
1363 uri = uris.erase(uri);
1364 else
1365 uri++;
1366
1367 // append to all URIs the keys for this sub-schema
1368 for (auto &key : keys)
1369 for (auto &uri : uris)
1370 uri = uri.append(key);
1371
1372 std::shared_ptr<::schema> sch;
1373
1374 // boolean schema
1375 if (schema.type() == json::value_t::boolean)
1376 sch = std::make_shared<boolean>(schema, root);
1377 else if (schema.type() == json::value_t::object) {
1378
1379 auto attr = schema.find("$id"); // if $id is present, this schema can be referenced by this ID
1380 // as an additional URI
1381 if (attr != schema.end()) {
1382 if (std::find(uris.begin(),
1383 uris.end(),
1384 attr.value().get<std::string>()) == uris.end())
1385 uris.push_back(uris.back().derive(attr.value().get<std::string>())); // so add it to the list if it is not there already
1386 schema.erase(attr);
1387 }
1388
1389 auto findDefinitions = [&](const std::string &defs) -> bool {
1390 attr = schema.find(defs);
1391 if (attr != schema.end()) {
1392 for (auto &def : attr.value().items())
1393 schema::make(def.value(), root, {defs, def.key()}, uris);
1394 schema.erase(attr);
1395 return true;
1396 }
1397 return false;
1398 };
1399 if (!findDefinitions("$defs")) {
1400 findDefinitions("definitions");
1401 }
1402
1403 attr = schema.find("$ref");
1404 if (attr != schema.end()) { // this schema is a reference
1405 // the last one on the uri-stack is the last id seen before coming here,
1406 // so this is the origial URI for this reference, the $ref-value has thus be resolved from it
1407 auto id = uris.back().derive(attr.value().get<std::string>());
1408 sch = root->get_or_create_ref(id);
1409
1410 schema.erase(attr);
1411
1412 // special case where we break draft-7 and allow overriding of properties when a $ref is used
1413 attr = schema.find("default");
1414 if (attr != schema.end()) {
1415 // copy the referenced schema depending on the underlying type and modify the default value
1416 if (auto new_sch = sch->make_for_default_(sch, root, uris, attr.value())) {
1417 sch = new_sch;
1418 }
1419 schema.erase(attr);
1420 }
1421 } else {
1422 sch = std::make_shared<type_schema>(schema, root, uris);
1423 }
1424
1425 schema.erase("$schema");
1426 schema.erase("title");
1427 schema.erase("description");
1428 } else {
1429 throw std::invalid_argument("invalid JSON-type for a schema for " + uris[0].to_string() + ", expected: boolean or object");
1430 }
1431
1432 for (auto &uri : uris) { // for all URIs this schema is referenced by
1433 root->insert(uri, sch);
1434
1435 if (schema.type() == json::value_t::object)
1436 for (auto &u : schema.items())
1437 root->insert_unknown_keyword(uri, u.key(), u.value()); // insert unknown keywords for later reference
1438 }
1439 return sch;
1440}
1441
1443{
1444 void error(const json::json_pointer &ptr, const json &instance, const std::string &message) override
1445 {
1446 throw std::invalid_argument(std::string("At ") + ptr.to_string() + " of " + instance.dump() + " - " + message + "\n");
1447 }
1448};
1449
1450} // namespace
1451
1452namespace nlohmann
1453{
1454namespace json_schema
1455{
1456
1458 format_checker format,
1459 content_checker content)
1460 : root_(std::unique_ptr<root_schema>(new root_schema(std::move(loader),
1461 std::move(format),
1462 std::move(content))))
1463{
1464}
1465
1466json_validator::json_validator(const json &schema,
1467 schema_loader loader,
1468 format_checker format,
1469 content_checker content)
1470 : json_validator(std::move(loader),
1471 std::move(format),
1472 std::move(content))
1473{
1474 set_root_schema(schema);
1475}
1476
1478 schema_loader loader,
1479 format_checker format,
1480 content_checker content)
1481
1482 : json_validator(std::move(loader),
1483 std::move(format),
1484 std::move(content))
1485{
1486 set_root_schema(std::move(schema));
1487}
1488
1489// move constructor, destructor and move assignment operator can be defaulted here
1490// where root_schema is a complete type
1492json_validator::~json_validator() = default;
1494
1495void json_validator::set_root_schema(const json &schema)
1496{
1497 root_->set_root_schema(schema);
1498}
1499
1501{
1502 root_->set_root_schema(std::move(schema));
1503}
1504
1505json json_validator::validate(const json &instance) const
1506{
1508 return validate(instance, err);
1509}
1510
1511json json_validator::validate(const json &instance, error_handler &err, const json_uri &initial_uri) const
1512{
1513 json::json_pointer ptr;
1514 json_patch patch;
1515 root_->validate(ptr, instance, patch, err, initial_uri);
1516 return patch;
1517}
1518
1519} // namespace json_schema
1520} // namespace nlohmann
std::vector< std::shared_ptr< schema > > items_
array(json &sch, root_schema *root, const std::vector< nlohmann::json_uri > &uris)
void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const override
void validate(const json::json_pointer &, const json &, json_patch &, error_handler &) const override
void validate(const json::json_pointer &ptr, const json &instance, json_patch &, error_handler &e) const override
void error(const json::json_pointer &ptr, const json &instance, const std::string &message) override
void error(const json::json_pointer &ptr, const json &instance, const std::string &message) override
void propagate(error_handler &e, const std::string &prefix) const
static bool is_validate_complete(const json &, const json::json_pointer &, error_handler &, const logical_combination_error_handler &, size_t, size_t)
std::vector< std::shared_ptr< schema > > subschemata_
bool is_validate_complete(const json &instance, const json::json_pointer &ptr, error_handler &e, const logical_combination_error_handler &, size_t count, size_t)
void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const final
bool is_validate_complete(const json &, const json::json_pointer &, error_handler &e, const logical_combination_error_handler &esub, size_t, size_t current_schema_index)
bool is_validate_complete(const json &, const json::json_pointer &, error_handler &, const logical_combination_error_handler &, size_t count, size_t)
logical_combination(json &sch, root_schema *root, const std::vector< nlohmann::json_uri > &uris)
const json & default_value(const json::json_pointer &ptr, const json &instance, error_handler &e) const override
logical_not(json &sch, root_schema *root, const std::vector< nlohmann::json_uri > &uris)
void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const final
void validate(const json::json_pointer &ptr, const json &instance, json_patch &, error_handler &e) const override
numeric(const json &sch, root_schema *root, std::set< std::string > &kw)
std::pair< bool, json::number_float_t > multipleOf_
void validate(const json::json_pointer &ptr, const json &instance, json_patch &, error_handler &e) const override
std::map< std::string, std::shared_ptr< schema > > dependencies_
void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const override
object(json &sch, root_schema *root, const std::vector< nlohmann::json_uri > &uris)
std::vector< std::pair< std::regex, std::shared_ptr< schema > > > patternProperties_
std::map< std::string, std::shared_ptr< schema > > properties_
void validate(const json::json_pointer &ptr, const json &instance, json_patch &, error_handler &e) const override final
required(const std::vector< std::string > &r, root_schema *root)
const json & default_value(const json::json_pointer &ptr, const json &instance, error_handler &e) const override final
virtual std::shared_ptr< schema > make_for_default_(std::shared_ptr<::schema > &sch, root_schema *root, std::vector< nlohmann::json_uri > &uris, nlohmann::json &default_value) const override
schema_ref(const std::string &id, root_schema *root)
void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const final
void set_target(const std::shared_ptr< schema > &target, bool strong=false)
virtual void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const =0
virtual const json & default_value(const json::json_pointer &, const json &, error_handler &) const
virtual std::shared_ptr< schema > make_for_default_(std::shared_ptr<::schema > &, root_schema *, std::vector< nlohmann::json_uri > &, nlohmann::json &) const
static std::shared_ptr< schema > make(json &schema, root_schema *root, const std::vector< std::string > &key, std::vector< nlohmann::json_uri > uris)
void validate(const json::json_pointer &ptr, const json &instance, json_patch &, error_handler &e) const override
std::tuple< bool, std::string, std::string > content_
std::size_t utf8_length(const std::string &s) const
void error(const json::json_pointer &ptr, const json &instance, const std::string &message) override
std::vector< std::shared_ptr< schema > > type_
virtual std::shared_ptr< schema > make_for_default_(std::shared_ptr<::schema > &, root_schema *, std::vector< nlohmann::json_uri > &, nlohmann::json &default_value) const override
type_schema(json &sch, root_schema *root, const std::vector< nlohmann::json_uri > &uris)
std::vector< std::shared_ptr< schema > > logic_
static std::shared_ptr< schema > make(json &schema, json::value_t type, root_schema *, const std::vector< nlohmann::json_uri > &, std::set< std::string > &)
void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const override final
json_patch & add(const json::json_pointer &, json value)
virtual void error(const json::json_pointer &, const json &, const std::string &)=0
std::unique_ptr< root_schema > root_
json_validator & operator=(json_validator &&)
json_validator(json &&, schema_loader=nullptr, format_checker=nullptr, content_checker=nullptr)
json_validator(schema_loader=nullptr, format_checker=nullptr, content_checker=nullptr)
json_validator(const json &, schema_loader=nullptr, format_checker=nullptr, content_checker=nullptr)
json validate(const json &, error_handler &, const json_uri &initial_uri=json_uri("#")) const
void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e, const json_uri &initial) const
void insert_unknown_keyword(const json_uri &uri, const std::string &key, json &value)
schema_file & get_or_create_file(const std::string &loc)
void insert(const json_uri &uri, const std::shared_ptr< schema > &s)
root_schema(schema_loader &&loader, format_checker &&format, content_checker &&content)
std::map< std::string, schema_file > files_
std::shared_ptr< schema > get_or_create_ref(const json_uri &uri)
std::shared_ptr< schema > root_
std::string fragment() const
std::string to_string() const
Definition json-uri.cpp:118
std::string location() const
Definition json-uri.cpp:102
const json::json_pointer & pointer() const
json_uri derive(const std::string &uri) const
json_uri append(const std::string &field) const
#define REGEX_NAMESPACE
const std::string logical_combination< anyOf >::key
const std::string logical_combination< allOf >::key
const std::string logical_combination< oneOf >::key
std::function< void(const json_uri &, json &)> schema_loader
std::function< void(const std::string &, const std::string &)> format_checker
std::function< void(const std::string &, const std::string &, const json &)> content_checker
std::string message_
json instance_
json::json_pointer ptr_
std::map< std::string, std::shared_ptr< schema_ref > > unresolved
std::map< std::string, std::shared_ptr< schema > > schemas