2 * Copyright 2017 Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include <folly/experimental/JSONSchema.h>
19 #include <boost/algorithm/string/replace.hpp>
20 #include <boost/regex.hpp>
21 #include <folly/Conv.h>
22 #include <folly/Memory.h>
23 #include <folly/Optional.h>
24 #include <folly/String.h>
25 #include <folly/Singleton.h>
26 #include <folly/json.h>
29 namespace jsonschema {
34 * We throw this exception when schema validation fails.
36 struct SchemaError : std::runtime_error {
38 SchemaError(SchemaError&&) = default;
39 SchemaError(const SchemaError&) = default;
41 SchemaError(folly::StringPiece expected, const dynamic& value)
42 : std::runtime_error(to<std::string>(
43 "Expected to get ", expected, " for value ", toJson(value))) {}
44 SchemaError(folly::StringPiece expected,
45 const dynamic& schema,
47 : std::runtime_error(to<std::string>("Expected to get ",
54 template <class... Args>
55 Optional<SchemaError> makeError(Args&&... args) {
56 return Optional<SchemaError>(SchemaError(std::forward<Args>(args)...));
59 struct ValidationContext;
62 virtual ~IValidator() = default;
65 friend struct ValidationContext;
67 virtual Optional<SchemaError> validate(ValidationContext&,
68 const dynamic& value) const = 0;
72 * This is a 'context' used only when executing the validators to validate some
73 * json. It keeps track of which validators have been executed on which json so
74 * we can detect infinite recursion.
76 struct ValidationContext {
77 Optional<SchemaError> validate(IValidator* validator, const dynamic& value) {
78 auto ret = seen.insert(std::make_pair(validator, &value));
80 throw std::runtime_error("Infinite recursion detected");
82 return validator->validate(*this, value);
86 std::unordered_set<std::pair<const IValidator*, const dynamic*>> seen;
90 * This is a 'context' used only when building the schema validators from a
91 * piece of json. It stores the original schema and the set of refs, so that we
92 * can have parts of the schema refer to other parts.
94 struct SchemaValidatorContext final {
95 explicit SchemaValidatorContext(const dynamic& s) : schema(s) {}
97 const dynamic& schema;
98 std::unordered_map<std::string, IValidator*> refs;
102 * Root validator for a schema.
104 struct SchemaValidator final : IValidator, public Validator {
105 SchemaValidator() = default;
106 void loadSchema(SchemaValidatorContext& context, const dynamic& schema);
108 Optional<SchemaError> validate(ValidationContext&,
109 const dynamic& value) const override;
111 // Validator interface
112 void validate(const dynamic& value) const override;
113 exception_wrapper try_validate(const dynamic& value) const noexcept override;
115 static std::unique_ptr<SchemaValidator> make(SchemaValidatorContext& context,
116 const dynamic& schema) {
117 // We break apart the constructor and actually loading the schema so that
118 // we can handle the case where a schema refers to itself, e.g. via
120 auto v = make_unique<SchemaValidator>();
121 v->loadSchema(context, schema);
126 std::vector<std::unique_ptr<IValidator>> validators_;
129 struct MultipleOfValidator final : IValidator {
130 explicit MultipleOfValidator(dynamic schema) : schema_(std::move(schema)) {}
131 Optional<SchemaError> validate(ValidationContext&,
132 const dynamic& value) const override {
133 if (!schema_.isNumber() || !value.isNumber()) {
136 if (schema_.isDouble() || value.isDouble()) {
137 const auto rem = std::remainder(value.asDouble(), schema_.asDouble());
138 if (std::abs(rem) > std::numeric_limits<double>::epsilon()) {
139 return makeError("a multiple of ", schema_, value);
141 } else { // both ints
142 if ((value.getInt() % schema_.getInt()) != 0) {
143 return makeError("a multiple of ", schema_, value);
151 struct ComparisonValidator final : IValidator {
152 enum class Type { MIN, MAX };
153 ComparisonValidator(dynamic schema, const dynamic* exclusive, Type type)
154 : schema_(std::move(schema)), exclusive_(false), type_(type) {
155 if (exclusive && exclusive->isBool()) {
156 exclusive_ = exclusive->getBool();
160 template <typename Numeric>
161 Optional<SchemaError> validateHelper(const dynamic& value,
164 if (type_ == Type::MIN) {
167 return makeError("greater than ", schema_, value);
171 return makeError("greater than or equal to ", schema_, value);
174 } else if (type_ == Type::MAX) {
177 return makeError("less than ", schema_, value);
181 return makeError("less than or equal to ", schema_, value);
188 Optional<SchemaError> validate(ValidationContext&,
189 const dynamic& value) const override {
190 if (!schema_.isNumber() || !value.isNumber()) {
193 if (schema_.isDouble() || value.isDouble()) {
194 return validateHelper(value, schema_.asDouble(), value.asDouble());
195 } else { // both ints
196 return validateHelper(value, schema_.asInt(), value.asInt());
205 template <class Comparison>
206 struct SizeValidator final : IValidator {
207 explicit SizeValidator(const dynamic& schema, dynamic::Type type)
208 : length_(-1), type_(type) {
209 if (schema.isInt()) {
210 length_ = schema.getInt();
214 Optional<SchemaError> validate(ValidationContext&,
215 const dynamic& value) const override {
219 if (value.type() != type_) {
222 if (!Comparison()(length_, int64_t(value.size()))) {
223 return makeError("different length string/array/object", value);
231 struct StringPatternValidator final : IValidator {
232 explicit StringPatternValidator(const dynamic& schema) {
233 if (schema.isString()) {
234 regex_ = boost::regex(schema.getString());
238 Optional<SchemaError> validate(ValidationContext&,
239 const dynamic& value) const override {
240 if (!value.isString() || regex_.empty()) {
243 if (!boost::regex_search(value.getString(), regex_)) {
244 return makeError("string matching regex", value);
251 struct ArrayUniqueValidator final : IValidator {
252 explicit ArrayUniqueValidator(const dynamic& schema) : unique_(false) {
253 if (schema.isBool()) {
254 unique_ = schema.getBool();
258 Optional<SchemaError> validate(ValidationContext&,
259 const dynamic& value) const override {
260 if (!unique_ || !value.isArray()) {
263 for (const auto& i : value) {
264 for (const auto& j : value) {
265 if (&i != &j && i == j) {
266 return makeError("unique items in array", value);
275 struct ArrayItemsValidator final : IValidator {
276 ArrayItemsValidator(SchemaValidatorContext& context,
277 const dynamic* items,
278 const dynamic* additionalItems)
279 : allowAdditionalItems_(true) {
280 if (items && items->isObject()) {
281 itemsValidator_ = SchemaValidator::make(context, *items);
282 return; // Additional items is ignored
283 } else if (items && items->isArray()) {
284 for (const auto& item : *items) {
285 itemsValidators_.emplace_back(SchemaValidator::make(context, item));
288 // If items isn't present or is invalid, it defaults to an empty schema.
289 itemsValidator_ = SchemaValidator::make(context, dynamic::object);
291 if (additionalItems) {
292 if (additionalItems->isBool()) {
293 allowAdditionalItems_ = additionalItems->getBool();
294 } else if (additionalItems->isObject()) {
295 additionalItemsValidator_ =
296 SchemaValidator::make(context, *additionalItems);
301 Optional<SchemaError> validate(ValidationContext& vc,
302 const dynamic& value) const override {
303 if (!value.isArray()) {
306 if (itemsValidator_) {
307 for (const auto& v : value) {
308 if (auto se = vc.validate(itemsValidator_.get(), v)) {
315 for (; pos < value.size() && pos < itemsValidators_.size(); ++pos) {
316 if (auto se = vc.validate(itemsValidators_[pos].get(), value[pos])) {
320 if (!allowAdditionalItems_ && pos < value.size()) {
321 return makeError("no more additional items", value);
323 if (additionalItemsValidator_) {
324 for (; pos < value.size(); ++pos) {
326 vc.validate(additionalItemsValidator_.get(), value[pos])) {
333 std::unique_ptr<IValidator> itemsValidator_;
334 std::vector<std::unique_ptr<IValidator>> itemsValidators_;
335 std::unique_ptr<IValidator> additionalItemsValidator_;
336 bool allowAdditionalItems_;
339 struct RequiredValidator final : IValidator {
340 explicit RequiredValidator(const dynamic& schema) {
341 if (schema.isArray()) {
342 for (const auto& item : schema) {
343 if (item.isString()) {
344 properties_.emplace_back(item.getString());
350 Optional<SchemaError> validate(ValidationContext&,
351 const dynamic& value) const override {
352 if (value.isObject()) {
353 for (const auto& prop : properties_) {
354 if (!value.get_ptr(prop)) {
355 return makeError("property ", prop, value);
363 std::vector<std::string> properties_;
366 struct PropertiesValidator final : IValidator {
367 PropertiesValidator(SchemaValidatorContext& context,
368 const dynamic* properties,
369 const dynamic* patternProperties,
370 const dynamic* additionalProperties)
371 : allowAdditionalProperties_(true) {
372 if (properties && properties->isObject()) {
373 for (const auto& pair : properties->items()) {
374 if (pair.first.isString()) {
375 propertyValidators_[pair.first.getString()] =
376 SchemaValidator::make(context, pair.second);
380 if (patternProperties && patternProperties->isObject()) {
381 for (const auto& pair : patternProperties->items()) {
382 if (pair.first.isString()) {
383 patternPropertyValidators_.emplace_back(
384 boost::regex(pair.first.getString()),
385 SchemaValidator::make(context, pair.second));
389 if (additionalProperties) {
390 if (additionalProperties->isBool()) {
391 allowAdditionalProperties_ = additionalProperties->getBool();
392 } else if (additionalProperties->isObject()) {
393 additionalPropertyValidator_ =
394 SchemaValidator::make(context, *additionalProperties);
399 Optional<SchemaError> validate(ValidationContext& vc,
400 const dynamic& value) const override {
401 if (!value.isObject()) {
404 for (const auto& pair : value.items()) {
405 if (!pair.first.isString()) {
408 const std::string& key = pair.first.getString();
409 auto it = propertyValidators_.find(key);
410 bool matched = false;
411 if (it != propertyValidators_.end()) {
412 if (auto se = vc.validate(it->second.get(), pair.second)) {
418 const std::string& strkey = key;
419 for (const auto& ppv : patternPropertyValidators_) {
420 if (boost::regex_search(strkey, ppv.first)) {
421 if (auto se = vc.validate(ppv.second.get(), pair.second)) {
430 if (!allowAdditionalProperties_) {
431 return makeError("no more additional properties", value);
433 if (additionalPropertyValidator_) {
435 vc.validate(additionalPropertyValidator_.get(), pair.second)) {
443 std::unordered_map<std::string, std::unique_ptr<IValidator>>
445 std::vector<std::pair<boost::regex, std::unique_ptr<IValidator>>>
446 patternPropertyValidators_;
447 std::unique_ptr<IValidator> additionalPropertyValidator_;
448 bool allowAdditionalProperties_;
451 struct DependencyValidator final : IValidator {
452 DependencyValidator(SchemaValidatorContext& context, const dynamic& schema) {
453 if (!schema.isObject()) {
456 for (const auto& pair : schema.items()) {
457 if (!pair.first.isString()) {
460 if (pair.second.isArray()) {
461 auto p = make_pair(pair.first.getString(), std::vector<std::string>());
462 for (const auto& item : pair.second) {
463 if (item.isString()) {
464 p.second.push_back(item.getString());
467 propertyDep_.emplace_back(std::move(p));
469 if (pair.second.isObject()) {
470 schemaDep_.emplace_back(pair.first.getString(),
471 SchemaValidator::make(context, pair.second));
476 Optional<SchemaError> validate(ValidationContext& vc,
477 const dynamic& value) const override {
478 if (!value.isObject()) {
481 for (const auto& pair : propertyDep_) {
482 if (value.count(pair.first)) {
483 for (const auto& prop : pair.second) {
484 if (!value.count(prop)) {
485 return makeError("property ", prop, value);
490 for (const auto& pair : schemaDep_) {
491 if (value.count(pair.first)) {
492 if (auto se = vc.validate(pair.second.get(), value)) {
500 std::vector<std::pair<std::string, std::vector<std::string>>> propertyDep_;
501 std::vector<std::pair<std::string, std::unique_ptr<IValidator>>> schemaDep_;
504 struct EnumValidator final : IValidator {
505 explicit EnumValidator(dynamic schema) : schema_(std::move(schema)) {}
507 Optional<SchemaError> validate(ValidationContext&,
508 const dynamic& value) const override {
509 if (!schema_.isArray()) {
512 for (const auto& item : schema_) {
517 return makeError("one of enum values: ", schema_, value);
522 struct TypeValidator final : IValidator {
523 explicit TypeValidator(const dynamic& schema) {
524 if (schema.isString()) {
525 addType(schema.stringPiece());
526 } else if (schema.isArray()) {
527 for (const auto& item : schema) {
528 if (item.isString()) {
529 addType(item.stringPiece());
535 Optional<SchemaError> validate(ValidationContext&,
536 const dynamic& value) const override {
538 std::find(allowedTypes_.begin(), allowedTypes_.end(), value.type());
539 if (it == allowedTypes_.end()) {
540 return makeError("a value of type ", typeStr_, value);
546 std::vector<dynamic::Type> allowedTypes_;
547 std::string typeStr_; // for errors
549 void addType(StringPiece value) {
550 if (value == "array") {
551 allowedTypes_.push_back(dynamic::Type::ARRAY);
552 } else if (value == "boolean") {
553 allowedTypes_.push_back(dynamic::Type::BOOL);
554 } else if (value == "integer") {
555 allowedTypes_.push_back(dynamic::Type::INT64);
556 } else if (value == "number") {
557 allowedTypes_.push_back(dynamic::Type::INT64);
558 allowedTypes_.push_back(dynamic::Type::DOUBLE);
559 } else if (value == "null") {
560 allowedTypes_.push_back(dynamic::Type::NULLT);
561 } else if (value == "object") {
562 allowedTypes_.push_back(dynamic::Type::OBJECT);
563 } else if (value == "string") {
564 allowedTypes_.push_back(dynamic::Type::STRING);
568 if (!typeStr_.empty()) {
571 typeStr_ += value.str();
575 struct AllOfValidator final : IValidator {
576 AllOfValidator(SchemaValidatorContext& context, const dynamic& schema) {
577 if (schema.isArray()) {
578 for (const auto& item : schema) {
579 validators_.emplace_back(SchemaValidator::make(context, item));
584 Optional<SchemaError> validate(ValidationContext& vc,
585 const dynamic& value) const override {
586 for (const auto& val : validators_) {
587 if (auto se = vc.validate(val.get(), value)) {
594 std::vector<std::unique_ptr<IValidator>> validators_;
597 struct AnyOfValidator final : IValidator {
598 enum class Type { EXACTLY_ONE, ONE_OR_MORE };
600 AnyOfValidator(SchemaValidatorContext& context,
601 const dynamic& schema,
604 if (schema.isArray()) {
605 for (const auto& item : schema) {
606 validators_.emplace_back(SchemaValidator::make(context, item));
611 Optional<SchemaError> validate(ValidationContext& vc,
612 const dynamic& value) const override {
613 std::vector<SchemaError> errors;
614 for (const auto& val : validators_) {
615 if (auto se = vc.validate(val.get(), value)) {
616 errors.emplace_back(*se);
619 const auto success = validators_.size() - errors.size();
621 return makeError("at least one valid schema", value);
622 } else if (success > 1 && type_ == Type::EXACTLY_ONE) {
623 return makeError("exactly one valid schema", value);
629 std::vector<std::unique_ptr<IValidator>> validators_;
632 struct RefValidator final : IValidator {
633 explicit RefValidator(IValidator* validator) : validator_(validator) {}
635 Optional<SchemaError> validate(ValidationContext& vc,
636 const dynamic& value) const override {
637 return vc.validate(validator_, value);
639 IValidator* validator_;
642 struct NotValidator final : IValidator {
643 NotValidator(SchemaValidatorContext& context, const dynamic& schema)
644 : validator_(SchemaValidator::make(context, schema)) {}
646 Optional<SchemaError> validate(ValidationContext& vc,
647 const dynamic& value) const override {
648 if (vc.validate(validator_.get(), value)) {
651 return makeError("Expected schema validation to fail", value);
653 std::unique_ptr<IValidator> validator_;
656 void SchemaValidator::loadSchema(SchemaValidatorContext& context,
657 const dynamic& schema) {
658 if (!schema.isObject() || schema.empty()) {
662 // Check for $ref, if we have one we won't apply anything else. Refs are
663 // pointers to other parts of the json, e.g. #/foo/bar points to the schema
664 // located at root["foo"]["bar"].
665 if (const auto* p = schema.get_ptr("$ref")) {
666 // We only support absolute refs, i.e. those starting with '#'
667 if (p->isString() && p->stringPiece()[0] == '#') {
668 auto it = context.refs.find(p->getString());
669 if (it != context.refs.end()) {
670 validators_.emplace_back(make_unique<RefValidator>(it->second));
674 // This is a ref, but we haven't loaded it yet. Find where it is based on
676 std::vector<std::string> parts;
677 split("/", p->stringPiece(), parts);
678 const auto* s = &context.schema; // First part is '#'
679 for (size_t i = 1; s && i < parts.size(); ++i) {
680 // Per the standard, we must replace ~1 with / and then ~0 with ~
681 boost::replace_all(parts[i], "~1", "/");
682 boost::replace_all(parts[i], "~0", "~");
684 s = s->get_ptr(parts[i]);
689 const size_t pos = to<size_t>(parts[i]);
690 if (pos < s->size()) {
694 } catch (const std::range_error&) {
700 // If you have a self-recursive reference, this avoids getting into an
701 // infinite recursion, where we try to load a schema that just references
702 // itself, and then we try to load it again, and so on.
703 // Instead we load a pointer to the schema into the refs, so that any
704 // future references to it will just see that pointer and won't try to
705 // keep parsing further.
707 auto v = make_unique<SchemaValidator>();
708 context.refs[p->getString()] = v.get();
709 v->loadSchema(context, *s);
710 validators_.emplace_back(std::move(v));
716 // Numeric validators
717 if (const auto* p = schema.get_ptr("multipleOf")) {
718 validators_.emplace_back(make_unique<MultipleOfValidator>(*p));
720 if (const auto* p = schema.get_ptr("maximum")) {
721 validators_.emplace_back(
722 make_unique<ComparisonValidator>(*p,
723 schema.get_ptr("exclusiveMaximum"),
724 ComparisonValidator::Type::MAX));
726 if (const auto* p = schema.get_ptr("minimum")) {
727 validators_.emplace_back(
728 make_unique<ComparisonValidator>(*p,
729 schema.get_ptr("exclusiveMinimum"),
730 ComparisonValidator::Type::MIN));
734 if (const auto* p = schema.get_ptr("maxLength")) {
735 validators_.emplace_back(
736 make_unique<SizeValidator<std::greater_equal<int64_t>>>(
737 *p, dynamic::Type::STRING));
739 if (const auto* p = schema.get_ptr("minLength")) {
740 validators_.emplace_back(
741 make_unique<SizeValidator<std::less_equal<int64_t>>>(
742 *p, dynamic::Type::STRING));
744 if (const auto* p = schema.get_ptr("pattern")) {
745 validators_.emplace_back(make_unique<StringPatternValidator>(*p));
749 const auto* items = schema.get_ptr("items");
750 const auto* additionalItems = schema.get_ptr("additionalItems");
751 if (items || additionalItems) {
752 validators_.emplace_back(
753 make_unique<ArrayItemsValidator>(context, items, additionalItems));
755 if (const auto* p = schema.get_ptr("maxItems")) {
756 validators_.emplace_back(
757 make_unique<SizeValidator<std::greater_equal<int64_t>>>(
758 *p, dynamic::Type::ARRAY));
760 if (const auto* p = schema.get_ptr("minItems")) {
761 validators_.emplace_back(
762 make_unique<SizeValidator<std::less_equal<int64_t>>>(
763 *p, dynamic::Type::ARRAY));
765 if (const auto* p = schema.get_ptr("uniqueItems")) {
766 validators_.emplace_back(make_unique<ArrayUniqueValidator>(*p));
770 const auto* properties = schema.get_ptr("properties");
771 const auto* patternProperties = schema.get_ptr("patternProperties");
772 const auto* additionalProperties = schema.get_ptr("additionalProperties");
773 if (properties || patternProperties || additionalProperties) {
774 validators_.emplace_back(make_unique<PropertiesValidator>(
775 context, properties, patternProperties, additionalProperties));
777 if (const auto* p = schema.get_ptr("maxProperties")) {
778 validators_.emplace_back(
779 make_unique<SizeValidator<std::greater_equal<int64_t>>>(
780 *p, dynamic::Type::OBJECT));
782 if (const auto* p = schema.get_ptr("minProperties")) {
783 validators_.emplace_back(
784 make_unique<SizeValidator<std::less_equal<int64_t>>>(
785 *p, dynamic::Type::OBJECT));
787 if (const auto* p = schema.get_ptr("required")) {
788 validators_.emplace_back(make_unique<RequiredValidator>(*p));
792 if (const auto* p = schema.get_ptr("dependencies")) {
793 validators_.emplace_back(make_unique<DependencyValidator>(context, *p));
795 if (const auto* p = schema.get_ptr("enum")) {
796 validators_.emplace_back(make_unique<EnumValidator>(*p));
798 if (const auto* p = schema.get_ptr("type")) {
799 validators_.emplace_back(make_unique<TypeValidator>(*p));
801 if (const auto* p = schema.get_ptr("allOf")) {
802 validators_.emplace_back(make_unique<AllOfValidator>(context, *p));
804 if (const auto* p = schema.get_ptr("anyOf")) {
805 validators_.emplace_back(make_unique<AnyOfValidator>(
806 context, *p, AnyOfValidator::Type::ONE_OR_MORE));
808 if (const auto* p = schema.get_ptr("oneOf")) {
809 validators_.emplace_back(make_unique<AnyOfValidator>(
810 context, *p, AnyOfValidator::Type::EXACTLY_ONE));
812 if (const auto* p = schema.get_ptr("not")) {
813 validators_.emplace_back(make_unique<NotValidator>(context, *p));
817 void SchemaValidator::validate(const dynamic& value) const {
818 ValidationContext vc;
819 if (auto se = validate(vc, value)) {
824 exception_wrapper SchemaValidator::try_validate(const dynamic& value) const
827 ValidationContext vc;
828 if (auto se = validate(vc, value)) {
829 return make_exception_wrapper<SchemaError>(*se);
831 } catch (const std::exception& e) {
832 return exception_wrapper(std::current_exception(), e);
834 return exception_wrapper(std::current_exception());
836 return exception_wrapper();
839 Optional<SchemaError> SchemaValidator::validate(ValidationContext& vc,
840 const dynamic& value) const {
841 for (const auto& validator : validators_) {
842 if (auto se = vc.validate(validator.get(), value)) {
850 * Metaschema, i.e. schema for schema.
851 * Inlined from the $schema url
853 const char* metaschemaJson =
856 \"id\": \"http://json-schema.org/draft-04/schema#\", \
857 \"$schema\": \"http://json-schema.org/draft-04/schema#\", \
858 \"description\": \"Core schema meta-schema\", \
861 \"type\": \"array\", \
863 \"items\": { \"$ref\": \"#\" } \
865 \"positiveInteger\": { \
866 \"type\": \"integer\", \
869 \"positiveIntegerDefault0\": { \
871 { \"$ref\": \"#/definitions/positiveInteger\" }, { \"default\": 0 } ]\
874 \"enum\": [ \"array\", \"boolean\", \"integer\", \
875 \"null\", \"number\", \"object\", \"string\" ] \
878 \"type\": \"array\", \
879 \"items\": { \"type\": \"string\" }, \
881 \"uniqueItems\": true \
884 \"type\": \"object\", \
887 \"type\": \"string\", \
888 \"format\": \"uri\" \
891 \"type\": \"string\", \
892 \"format\": \"uri\" \
895 \"type\": \"string\" \
898 \"type\": \"string\" \
902 \"type\": \"number\", \
904 \"exclusiveMinimum\": true \
907 \"type\": \"number\" \
909 \"exclusiveMaximum\": { \
910 \"type\": \"boolean\", \
914 \"type\": \"number\" \
916 \"exclusiveMinimum\": { \
917 \"type\": \"boolean\", \
920 \"maxLength\": { \"$ref\": \"#/definitions/positiveInteger\" }, \
921 \"minLength\": { \"$ref\": \"#/definitions/positiveIntegerDefault0\" },\
923 \"type\": \"string\", \
924 \"format\": \"regex\" \
926 \"additionalItems\": { \
928 { \"type\": \"boolean\" }, \
929 { \"$ref\": \"#\" } \
935 { \"$ref\": \"#\" }, \
936 { \"$ref\": \"#/definitions/schemaArray\" } \
940 \"maxItems\": { \"$ref\": \"#/definitions/positiveInteger\" }, \
941 \"minItems\": { \"$ref\": \"#/definitions/positiveIntegerDefault0\" }, \
943 \"type\": \"boolean\", \
946 \"maxProperties\": { \"$ref\": \"#/definitions/positiveInteger\" }, \
947 \"minProperties\": { \
948 \"$ref\": \"#/definitions/positiveIntegerDefault0\" }, \
949 \"required\": { \"$ref\": \"#/definitions/stringArray\" }, \
950 \"additionalProperties\": { \
952 { \"type\": \"boolean\" }, \
953 { \"$ref\": \"#\" } \
958 \"type\": \"object\", \
959 \"additionalProperties\": { \"$ref\": \"#\" }, \
963 \"type\": \"object\", \
964 \"additionalProperties\": { \"$ref\": \"#\" }, \
967 \"patternProperties\": { \
968 \"type\": \"object\", \
969 \"additionalProperties\": { \"$ref\": \"#\" }, \
972 \"dependencies\": { \
973 \"type\": \"object\", \
974 \"additionalProperties\": { \
976 { \"$ref\": \"#\" }, \
977 { \"$ref\": \"#/definitions/stringArray\" } \
982 \"type\": \"array\", \
984 \"uniqueItems\": true \
988 { \"$ref\": \"#/definitions/simpleTypes\" }, \
990 \"type\": \"array\", \
991 \"items\": { \"$ref\": \"#/definitions/simpleTypes\" }, \
993 \"uniqueItems\": true \
997 \"allOf\": { \"$ref\": \"#/definitions/schemaArray\" }, \
998 \"anyOf\": { \"$ref\": \"#/definitions/schemaArray\" }, \
999 \"oneOf\": { \"$ref\": \"#/definitions/schemaArray\" }, \
1000 \"not\": { \"$ref\": \"#\" } \
1002 \"dependencies\": { \
1003 \"exclusiveMaximum\": [ \"maximum\" ], \
1004 \"exclusiveMinimum\": [ \"minimum\" ] \
1009 folly::Singleton<Validator> schemaValidator([]() {
1010 return makeValidator(parseJson(metaschemaJson)).release();
1014 Validator::~Validator() = default;
1016 std::unique_ptr<Validator> makeValidator(const dynamic& schema) {
1017 auto v = make_unique<SchemaValidator>();
1018 SchemaValidatorContext context(schema);
1019 context.refs["#"] = v.get();
1020 v->loadSchema(context, schema);
1021 return std::move(v);
1024 std::shared_ptr<Validator> makeSchemaValidator() {
1025 return schemaValidator.try_get();