18 #include <boost/algorithm/string/replace.hpp> 19 #include <boost/regex.hpp> 30 namespace jsonschema {
38 SchemaError(SchemaError&&) =
default;
39 SchemaError(
const SchemaError&) =
default;
42 : std::runtime_error(to<std::string>(
51 : std::runtime_error(to<std::string>(
59 template <
class...
Args>
64 struct ValidationContext;
67 virtual ~IValidator() =
default;
70 friend struct ValidationContext;
82 struct ValidationContext {
84 auto ret =
seen.insert(std::make_pair(validator, &value));
86 throw std::runtime_error(
"Infinite recursion detected");
88 return validator->validate(*
this, value);
92 std::unordered_set<std::pair<const IValidator*, const dynamic*>>
seen;
100 struct SchemaValidatorContext final {
101 explicit SchemaValidatorContext(
const dynamic&
s) :
schema(s) {}
104 std::unordered_map<std::string, IValidator*>
refs;
110 struct SchemaValidator final : IValidator,
public Validator {
111 SchemaValidator() =
default;
112 void loadSchema(SchemaValidatorContext&
context,
const dynamic&
schema);
114 Optional<SchemaError> validate(ValidationContext&,
const dynamic&
value)
118 void validate(
const dynamic& value)
const override;
119 exception_wrapper try_validate(
const dynamic& value)
const noexcept override;
121 static std::unique_ptr<SchemaValidator> make(
122 SchemaValidatorContext& context,
123 const dynamic& schema) {
127 auto v = std::make_unique<SchemaValidator>();
128 v->loadSchema(context, schema);
136 struct MultipleOfValidator final : IValidator {
138 Optional<SchemaError> validate(ValidationContext&,
const dynamic&
value)
140 if (!
schema_.isNumber() || !value.isNumber()) {
143 if (
schema_.isDouble() || value.isDouble()) {
144 const auto rem = std::remainder(value.asDouble(),
schema_.asDouble());
145 if (std::abs(rem) > std::numeric_limits<double>::epsilon()) {
146 return makeError(
"a multiple of ",
schema_, value);
149 if ((value.getInt() %
schema_.getInt()) != 0) {
150 return makeError(
"a multiple of ",
schema_, value);
158 struct ComparisonValidator final : IValidator {
160 ComparisonValidator(dynamic
schema,
const dynamic* exclusive,
Type type)
162 if (exclusive && exclusive->isBool()) {
167 template <
typename Numeric>
168 Optional<SchemaError>
169 validateHelper(
const dynamic&
value, Numeric
s, Numeric
v)
const {
173 return makeError(
"greater than ",
schema_, value);
177 return makeError(
"greater than or equal to ",
schema_, value);
180 }
else if (
type_ == Type::MAX) {
183 return makeError(
"less than ",
schema_, value);
187 return makeError(
"less than or equal to ",
schema_, value);
194 Optional<SchemaError> validate(ValidationContext&,
const dynamic& value)
196 if (!
schema_.isNumber() || !value.isNumber()) {
199 if (
schema_.isDouble() || value.isDouble()) {
200 return validateHelper(value,
schema_.asDouble(), value.asDouble());
202 return validateHelper(value,
schema_.asInt(), value.asInt());
211 template <
class Comparison>
212 struct SizeValidator final : IValidator {
215 if (schema.isInt()) {
220 Optional<SchemaError> validate(ValidationContext&,
const dynamic&
value)
225 if (value.type() !=
type_) {
229 return makeError(
"different length string/array/object", value);
237 struct StringPatternValidator final : IValidator {
238 explicit StringPatternValidator(
const dynamic&
schema) {
239 if (schema.isString()) {
244 Optional<SchemaError> validate(ValidationContext&,
const dynamic&
value)
246 if (!value.isString() ||
regex_.empty()) {
249 if (!boost::regex_search(value.getString(),
regex_)) {
250 return makeError(
"string matching regex", value);
257 struct ArrayUniqueValidator final : IValidator {
258 explicit ArrayUniqueValidator(
const dynamic&
schema) :
unique_(
false) {
259 if (schema.isBool()) {
264 Optional<SchemaError> validate(ValidationContext&,
const dynamic&
value)
266 if (!
unique_ || !value.isArray()) {
269 for (
const auto&
i : value) {
270 for (
const auto& j : value) {
271 if (&
i != &j &&
i == j) {
272 return makeError(
"unique items in array", value);
281 struct ArrayItemsValidator final : IValidator {
283 SchemaValidatorContext&
context,
284 const dynamic* items,
285 const dynamic* additionalItems)
287 if (items && items->isObject()) {
290 }
else if (items && items->isArray()) {
291 for (
const auto& item : *items) {
298 if (additionalItems) {
299 if (additionalItems->isBool()) {
301 }
else if (additionalItems->isObject()) {
303 SchemaValidator::make(context, *additionalItems);
308 Optional<SchemaError> validate(ValidationContext& vc,
const dynamic&
value)
310 if (!value.isArray()) {
314 for (
const auto&
v : value) {
328 return makeError(
"no more additional items", value);
331 for (; pos < value.size(); ++pos) {
346 struct RequiredValidator final : IValidator {
347 explicit RequiredValidator(
const dynamic&
schema) {
348 if (schema.isArray()) {
349 for (
const auto& item : schema) {
350 if (item.isString()) {
357 Optional<SchemaError> validate(ValidationContext&,
const dynamic&
value)
359 if (value.isObject()) {
361 if (!value.get_ptr(prop)) {
362 return makeError(
"property ", prop, value);
373 struct PropertiesValidator final : IValidator {
375 SchemaValidatorContext&
context,
376 const dynamic* properties,
377 const dynamic* patternProperties,
378 const dynamic* additionalProperties)
380 if (properties && properties->isObject()) {
381 for (
const auto&
pair : properties->items()) {
382 if (
pair.first.isString()) {
384 SchemaValidator::make(context,
pair.second);
388 if (patternProperties && patternProperties->isObject()) {
389 for (
const auto&
pair : patternProperties->items()) {
390 if (
pair.first.isString()) {
393 SchemaValidator::make(context,
pair.second));
397 if (additionalProperties) {
398 if (additionalProperties->isBool()) {
400 }
else if (additionalProperties->isObject()) {
402 SchemaValidator::make(context, *additionalProperties);
407 Optional<SchemaError> validate(ValidationContext& vc,
const dynamic&
value)
409 if (!value.isObject()) {
412 for (
const auto&
pair : value.items()) {
413 if (!
pair.first.isString()) {
418 bool matched =
false;
420 if (
auto se = vc.validate(it->second.get(),
pair.second)) {
428 if (boost::regex_search(strkey, ppv.first)) {
429 if (
auto se = vc.validate(ppv.second.get(),
pair.second)) {
439 return makeError(
"no more additional properties", value);
451 std::unordered_map<std::string, std::unique_ptr<IValidator>>
453 std::vector<std::pair<boost::regex, std::unique_ptr<IValidator>>>
459 struct DependencyValidator final : IValidator {
460 DependencyValidator(SchemaValidatorContext&
context,
const dynamic&
schema) {
461 if (!schema.isObject()) {
464 for (
const auto&
pair : schema.items()) {
465 if (!
pair.first.isString()) {
468 if (
pair.second.isArray()) {
469 auto p = make_pair(
pair.first.getString(), std::vector<std::string>());
470 for (
const auto& item :
pair.second) {
471 if (item.isString()) {
472 p.second.push_back(item.getString());
477 if (
pair.second.isObject()) {
479 pair.first.getString(),
480 SchemaValidator::make(context,
pair.second));
485 Optional<SchemaError> validate(ValidationContext& vc,
const dynamic&
value)
487 if (!value.isObject()) {
491 if (value.count(
pair.first)) {
492 for (
const auto& prop :
pair.second) {
493 if (!value.count(prop)) {
494 return makeError(
"property ", prop, value);
500 if (value.count(
pair.first)) {
501 if (
auto se = vc.validate(
pair.second.get(),
value)) {
509 std::vector<std::pair<std::string, std::vector<std::string>>>
propertyDep_;
510 std::vector<std::pair<std::string, std::unique_ptr<IValidator>>>
schemaDep_;
513 struct EnumValidator final : IValidator {
516 Optional<SchemaError> validate(ValidationContext&,
const dynamic&
value)
521 for (
const auto& item :
schema_) {
526 return makeError(
"one of enum values: ", schema_, value);
531 struct TypeValidator final : IValidator {
532 explicit TypeValidator(
const dynamic&
schema) {
533 if (schema.isString()) {
534 addType(schema.stringPiece());
535 }
else if (schema.isArray()) {
536 for (
const auto& item : schema) {
537 if (item.isString()) {
538 addType(item.stringPiece());
544 Optional<SchemaError> validate(ValidationContext&,
const dynamic&
value)
549 return makeError(
"a value of type ",
typeStr_, value);
559 if (value ==
"array") {
560 allowedTypes_.push_back(dynamic::Type::ARRAY);
561 }
else if (value ==
"boolean") {
562 allowedTypes_.push_back(dynamic::Type::BOOL);
563 }
else if (value ==
"integer") {
564 allowedTypes_.push_back(dynamic::Type::INT64);
565 }
else if (value ==
"number") {
566 allowedTypes_.push_back(dynamic::Type::INT64);
567 allowedTypes_.push_back(dynamic::Type::DOUBLE);
568 }
else if (value ==
"null") {
569 allowedTypes_.push_back(dynamic::Type::NULLT);
570 }
else if (value ==
"object") {
571 allowedTypes_.push_back(dynamic::Type::OBJECT);
572 }
else if (value ==
"string") {
577 if (!typeStr_.empty()) {
580 typeStr_ += value.str();
584 struct AllOfValidator final : IValidator {
585 AllOfValidator(SchemaValidatorContext&
context,
const dynamic&
schema) {
586 if (schema.isArray()) {
587 for (
const auto& item : schema) {
588 validators_.emplace_back(SchemaValidator::make(context, item));
593 Optional<SchemaError> validate(ValidationContext& vc,
const dynamic&
value)
596 if (
auto se = vc.validate(
val.get(),
value)) {
603 std::vector<std::unique_ptr<IValidator>>
validators_;
606 struct AnyOfValidator final : IValidator {
607 enum class Type { EXACTLY_ONE, ONE_OR_MORE };
610 SchemaValidatorContext&
context,
614 if (schema.isArray()) {
615 for (
const auto& item : schema) {
616 validators_.emplace_back(SchemaValidator::make(context, item));
621 Optional<SchemaError> validate(ValidationContext& vc,
const dynamic&
value)
623 std::vector<SchemaError> errors;
625 if (
auto se = vc.validate(
val.get(),
value)) {
626 errors.emplace_back(*se);
629 const auto success = validators_.size() - errors.size();
631 return makeError(
"at least one valid schema", value);
632 }
else if (success > 1 &&
type_ == Type::EXACTLY_ONE) {
633 return makeError(
"exactly one valid schema", value);
639 std::vector<std::unique_ptr<IValidator>>
validators_;
642 struct RefValidator final : IValidator {
643 explicit RefValidator(IValidator* validator) :
validator_(validator) {}
645 Optional<SchemaError> validate(ValidationContext& vc,
const dynamic&
value)
652 struct NotValidator final : IValidator {
653 NotValidator(SchemaValidatorContext&
context,
const dynamic&
schema)
654 :
validator_(SchemaValidator::make(context, schema)) {}
656 Optional<SchemaError> validate(ValidationContext& vc,
const dynamic&
value)
661 return makeError(
"Expected schema validation to fail", value);
666 void SchemaValidator::loadSchema(
667 SchemaValidatorContext&
context,
669 if (!schema.isObject() || schema.empty()) {
676 if (
const auto* p = schema.get_ptr(
"$ref")) {
678 if (p->isString() && p->stringPiece()[0] ==
'#') {
679 auto it = context.refs.find(p->getString());
680 if (it != context.refs.end()) {
681 validators_.emplace_back(std::make_unique<RefValidator>(it->second));
687 std::vector<std::string> parts;
688 split(
"/", p->stringPiece(), parts);
689 const auto*
s = &context.schema;
690 for (
size_t i = 1; s &&
i < parts.size(); ++
i) {
692 boost::replace_all(parts[
i],
"~1",
"/");
693 boost::replace_all(parts[i],
"~0",
"~");
695 s = s->get_ptr(parts[i]);
700 const size_t pos = to<size_t>(parts[
i]);
701 if (pos < s->
size()) {
705 }
catch (
const std::range_error&) {
718 auto v = std::make_unique<SchemaValidator>();
719 context.refs[p->getString()] =
v.get();
720 v->loadSchema(context, *s);
728 if (
const auto* p = schema.get_ptr(
"multipleOf")) {
729 validators_.emplace_back(std::make_unique<MultipleOfValidator>(*p));
731 if (
const auto* p = schema.get_ptr(
"maximum")) {
732 validators_.emplace_back(std::make_unique<ComparisonValidator>(
734 schema.get_ptr(
"exclusiveMaximum"),
735 ComparisonValidator::Type::MAX));
737 if (
const auto* p = schema.get_ptr(
"minimum")) {
738 validators_.emplace_back(std::make_unique<ComparisonValidator>(
740 schema.get_ptr(
"exclusiveMinimum"),
745 if (
const auto* p = schema.get_ptr(
"maxLength")) {
750 if (
const auto* p = schema.get_ptr(
"minLength")) {
755 if (
const auto* p = schema.get_ptr(
"pattern")) {
756 validators_.emplace_back(std::make_unique<StringPatternValidator>(*p));
760 const auto* items = schema.get_ptr(
"items");
761 const auto* additionalItems = schema.get_ptr(
"additionalItems");
762 if (items || additionalItems) {
764 std::make_unique<ArrayItemsValidator>(context, items, additionalItems));
766 if (
const auto* p = schema.get_ptr(
"maxItems")) {
769 *p, dynamic::Type::ARRAY));
771 if (
const auto* p = schema.get_ptr(
"minItems")) {
774 *p, dynamic::Type::ARRAY));
776 if (
const auto* p = schema.get_ptr(
"uniqueItems")) {
777 validators_.emplace_back(std::make_unique<ArrayUniqueValidator>(*p));
781 const auto* properties = schema.get_ptr(
"properties");
782 const auto* patternProperties = schema.get_ptr(
"patternProperties");
783 const auto* additionalProperties = schema.get_ptr(
"additionalProperties");
784 if (properties || patternProperties || additionalProperties) {
785 validators_.emplace_back(std::make_unique<PropertiesValidator>(
786 context, properties, patternProperties, additionalProperties));
788 if (
const auto* p = schema.get_ptr(
"maxProperties")) {
791 *p, dynamic::Type::OBJECT));
793 if (
const auto* p = schema.get_ptr(
"minProperties")) {
796 *p, dynamic::Type::OBJECT));
798 if (
const auto* p = schema.get_ptr(
"required")) {
799 validators_.emplace_back(std::make_unique<RequiredValidator>(*p));
803 if (
const auto* p = schema.get_ptr(
"dependencies")) {
805 std::make_unique<DependencyValidator>(context, *p));
807 if (
const auto* p = schema.get_ptr(
"enum")) {
808 validators_.emplace_back(std::make_unique<EnumValidator>(*p));
810 if (
const auto* p = schema.get_ptr(
"type")) {
811 validators_.emplace_back(std::make_unique<TypeValidator>(*p));
813 if (
const auto* p = schema.get_ptr(
"allOf")) {
814 validators_.emplace_back(std::make_unique<AllOfValidator>(context, *p));
816 if (
const auto* p = schema.get_ptr(
"anyOf")) {
817 validators_.emplace_back(std::make_unique<AnyOfValidator>(
818 context, *p, AnyOfValidator::Type::ONE_OR_MORE));
820 if (
const auto* p = schema.get_ptr(
"oneOf")) {
821 validators_.emplace_back(std::make_unique<AnyOfValidator>(
822 context, *p, AnyOfValidator::Type::EXACTLY_ONE));
824 if (
const auto* p = schema.get_ptr(
"not")) {
825 validators_.emplace_back(std::make_unique<NotValidator>(context, *p));
829 void SchemaValidator::validate(
const dynamic&
value)
const {
830 ValidationContext vc;
831 if (
auto se = validate(vc, value)) {
836 exception_wrapper SchemaValidator::try_validate(
const dynamic& value)
const 839 ValidationContext vc;
840 if (
auto se = validate(vc, value)) {
841 return make_exception_wrapper<SchemaError>(*se);
843 }
catch (
const std::exception& e) {
844 return exception_wrapper(std::current_exception(), e);
846 return exception_wrapper(std::current_exception());
848 return exception_wrapper();
851 Optional<SchemaError> SchemaValidator::validate(
852 ValidationContext& vc,
853 const dynamic& value)
const {
855 if (
auto se = vc.validate(validator.get(),
value)) {
866 const char* metaschemaJson =
869 \"id\": \"http://json-schema.org/draft-04/schema#\", \ 870 \"$schema\": \"http://json-schema.org/draft-04/schema#\", \ 871 \"description\": \"Core schema meta-schema\", \ 874 \"type\": \"array\", \ 876 \"items\": { \"$ref\": \"#\" } \ 878 \"positiveInteger\": { \ 879 \"type\": \"integer\", \ 882 \"positiveIntegerDefault0\": { \ 884 { \"$ref\": \"#/definitions/positiveInteger\" }, { \"default\": 0 } ]\ 887 \"enum\": [ \"array\", \"boolean\", \"integer\", \ 888 \"null\", \"number\", \"object\", \"string\" ] \ 891 \"type\": \"array\", \ 892 \"items\": { \"type\": \"string\" }, \ 894 \"uniqueItems\": true \ 897 \"type\": \"object\", \ 900 \"type\": \"string\", \ 901 \"format\": \"uri\" \ 904 \"type\": \"string\", \ 905 \"format\": \"uri\" \ 908 \"type\": \"string\" \ 911 \"type\": \"string\" \ 915 \"type\": \"number\", \ 917 \"exclusiveMinimum\": true \ 920 \"type\": \"number\" \ 922 \"exclusiveMaximum\": { \ 923 \"type\": \"boolean\", \ 927 \"type\": \"number\" \ 929 \"exclusiveMinimum\": { \ 930 \"type\": \"boolean\", \ 933 \"maxLength\": { \"$ref\": \"#/definitions/positiveInteger\" }, \ 934 \"minLength\": { \"$ref\": \"#/definitions/positiveIntegerDefault0\" },\ 936 \"type\": \"string\", \ 937 \"format\": \"regex\" \ 939 \"additionalItems\": { \ 941 { \"type\": \"boolean\" }, \ 942 { \"$ref\": \"#\" } \ 948 { \"$ref\": \"#\" }, \ 949 { \"$ref\": \"#/definitions/schemaArray\" } \ 953 \"maxItems\": { \"$ref\": \"#/definitions/positiveInteger\" }, \ 954 \"minItems\": { \"$ref\": \"#/definitions/positiveIntegerDefault0\" }, \ 956 \"type\": \"boolean\", \ 959 \"maxProperties\": { \"$ref\": \"#/definitions/positiveInteger\" }, \ 960 \"minProperties\": { \ 961 \"$ref\": \"#/definitions/positiveIntegerDefault0\" }, \ 962 \"required\": { \"$ref\": \"#/definitions/stringArray\" }, \ 963 \"additionalProperties\": { \ 965 { \"type\": \"boolean\" }, \ 966 { \"$ref\": \"#\" } \ 971 \"type\": \"object\", \ 972 \"additionalProperties\": { \"$ref\": \"#\" }, \ 976 \"type\": \"object\", \ 977 \"additionalProperties\": { \"$ref\": \"#\" }, \ 980 \"patternProperties\": { \ 981 \"type\": \"object\", \ 982 \"additionalProperties\": { \"$ref\": \"#\" }, \ 985 \"dependencies\": { \ 986 \"type\": \"object\", \ 987 \"additionalProperties\": { \ 989 { \"$ref\": \"#\" }, \ 990 { \"$ref\": \"#/definitions/stringArray\" } \ 995 \"type\": \"array\", \ 997 \"uniqueItems\": true \ 1001 { \"$ref\": \"#/definitions/simpleTypes\" }, \ 1003 \"type\": \"array\", \ 1004 \"items\": { \"$ref\": \"#/definitions/simpleTypes\" }, \ 1006 \"uniqueItems\": true \ 1010 \"allOf\": { \"$ref\": \"#/definitions/schemaArray\" }, \ 1011 \"anyOf\": { \"$ref\": \"#/definitions/schemaArray\" }, \ 1012 \"oneOf\": { \"$ref\": \"#/definitions/schemaArray\" }, \ 1013 \"not\": { \"$ref\": \"#\" } \ 1015 \"dependencies\": { \ 1016 \"exclusiveMaximum\": [ \"maximum\" ], \ 1017 \"exclusiveMinimum\": [ \"minimum\" ] \ 1030 auto v = std::make_unique<SchemaValidator>();
1031 SchemaValidatorContext
context(schema);
1032 context.refs[
"#"] =
v.get();
1033 v->loadSchema(context, schema);
1038 return schemaValidator.try_get();
std::vector< std::string > properties_
static ObjectMaker object()
dynamic parseJson(StringPiece range)
std::shared_ptr< Validator > makeSchemaValidator()
std::unique_ptr< IValidator > itemsValidator_
std::unordered_map< std::string, std::unique_ptr< IValidator > > propertyValidators_
constexpr detail::Map< Move > move
std::unordered_map< std::string, IValidator * > refs
std::vector< std::unique_ptr< IValidator > > validators_
internal::ArgsMatcher< InnerMatcher > Args(const InnerMatcher &matcher)
std::vector< std::unique_ptr< IValidator > > itemsValidators_
—— Concurrent Priority Queue Implementation ——
requires E e noexcept(noexcept(s.error(std::move(e))))
void split(const Delim &delimiter, const String &input, std::vector< OutputType > &out, bool ignoreEmpty)
std::unordered_set< std::pair< const IValidator *, const dynamic * > > seen
constexpr auto size(C const &c) -> decltype(c.size())
bool allowAdditionalProperties_
std::unique_ptr< Validator > makeValidator(const dynamic &schema)
std::vector< std::pair< std::string, std::unique_ptr< IValidator > > > schemaDep_
std::unique_ptr< IValidator > additionalItemsValidator_
std::vector< std::pair< std::string, std::vector< std::string > > > propertyDep_
std::unique_ptr< IValidator > additionalPropertyValidator_
std::enable_if<!std::is_array< T >::value, std::unique_ptr< T > >::type make_unique(Args &&...args)
std::vector< dynamic::Type > allowedTypes_
uint64_t value(const typename LockFreeRingBuffer< T, Atom >::Cursor &rbcursor)
std::vector< std::pair< boost::regex, std::unique_ptr< IValidator > > > patternPropertyValidators_
Range< const char * > StringPiece
std::string toJson(dynamic const &dyn)
bool allowAdditionalItems_