2 * Copyright 2015 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<fbstring, 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_, 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().toStdString());
238 Optional<SchemaError> validate(ValidationContext&,
239 const dynamic& value) const override {
240 if (!value.isString() || regex_.empty()) {
243 if (!boost::regex_search(value.getString().toStdString(), 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<fbstring> 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().toStdString()),
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 fbstring& 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.toStdString();
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<fbstring, std::unique_ptr<IValidator>> propertyValidators_;
444 std::vector<std::pair<boost::regex, std::unique_ptr<IValidator>>>
445 patternPropertyValidators_;
446 std::unique_ptr<IValidator> additionalPropertyValidator_;
447 bool allowAdditionalProperties_;
450 struct DependencyValidator final : IValidator {
451 DependencyValidator(SchemaValidatorContext& context, const dynamic& schema) {
452 if (!schema.isObject()) {
455 for (const auto& pair : schema.items()) {
456 if (!pair.first.isString()) {
459 if (pair.second.isArray()) {
460 auto p = make_pair(pair.first.getString(), std::vector<fbstring>());
461 for (const auto& item : pair.second) {
462 if (item.isString()) {
463 p.second.push_back(item.getString());
466 propertyDep_.emplace_back(std::move(p));
468 if (pair.second.isObject()) {
469 schemaDep_.emplace_back(pair.first.getString(),
470 SchemaValidator::make(context, pair.second));
475 Optional<SchemaError> validate(ValidationContext& vc,
476 const dynamic& value) const override {
477 if (!value.isObject()) {
480 for (const auto& pair : propertyDep_) {
481 if (value.count(pair.first)) {
482 for (const auto& prop : pair.second) {
483 if (!value.count(prop)) {
484 return makeError("property ", prop, value);
489 for (const auto& pair : schemaDep_) {
490 if (value.count(pair.first)) {
491 if (auto se = vc.validate(pair.second.get(), value)) {
499 std::vector<std::pair<fbstring, std::vector<fbstring>>> propertyDep_;
500 std::vector<std::pair<fbstring, std::unique_ptr<IValidator>>> schemaDep_;
503 struct EnumValidator final : IValidator {
504 explicit EnumValidator(dynamic schema) : schema_(std::move(schema)) {}
506 Optional<SchemaError> validate(ValidationContext&,
507 const dynamic& value) const override {
508 if (!schema_.isArray()) {
511 for (const auto& item : schema_) {
516 return makeError("one of enum values: ", schema_, value);
521 struct TypeValidator final : IValidator {
522 explicit TypeValidator(const dynamic& schema) {
523 if (schema.isString()) {
524 addType(schema.stringPiece());
525 } else if (schema.isArray()) {
526 for (const auto& item : schema) {
527 if (item.isString()) {
528 addType(item.stringPiece());
534 Optional<SchemaError> validate(ValidationContext&,
535 const dynamic& value) const override {
537 std::find(allowedTypes_.begin(), allowedTypes_.end(), value.type());
538 if (it == allowedTypes_.end()) {
539 return makeError("a value of type ", typeStr_, value);
545 std::vector<dynamic::Type> allowedTypes_;
546 std::string typeStr_; // for errors
548 void addType(StringPiece value) {
549 if (value == "array") {
550 allowedTypes_.push_back(dynamic::Type::ARRAY);
551 } else if (value == "boolean") {
552 allowedTypes_.push_back(dynamic::Type::BOOL);
553 } else if (value == "integer") {
554 allowedTypes_.push_back(dynamic::Type::INT64);
555 } else if (value == "number") {
556 allowedTypes_.push_back(dynamic::Type::INT64);
557 allowedTypes_.push_back(dynamic::Type::DOUBLE);
558 } else if (value == "null") {
559 allowedTypes_.push_back(dynamic::Type::NULLT);
560 } else if (value == "object") {
561 allowedTypes_.push_back(dynamic::Type::OBJECT);
562 } else if (value == "string") {
563 allowedTypes_.push_back(dynamic::Type::STRING);
567 if (!typeStr_.empty()) {
570 typeStr_ += value.str();
574 struct AllOfValidator final : IValidator {
575 AllOfValidator(SchemaValidatorContext& context, const dynamic& schema) {
576 if (schema.isArray()) {
577 for (const auto& item : schema) {
578 validators_.emplace_back(SchemaValidator::make(context, item));
583 Optional<SchemaError> validate(ValidationContext& vc,
584 const dynamic& value) const override {
585 for (const auto& val : validators_) {
586 if (auto se = vc.validate(val.get(), value)) {
593 std::vector<std::unique_ptr<IValidator>> validators_;
596 struct AnyOfValidator final : IValidator {
597 enum class Type { EXACTLY_ONE, ONE_OR_MORE };
599 AnyOfValidator(SchemaValidatorContext& context,
600 const dynamic& schema,
603 if (schema.isArray()) {
604 for (const auto& item : schema) {
605 validators_.emplace_back(SchemaValidator::make(context, item));
610 Optional<SchemaError> validate(ValidationContext& vc,
611 const dynamic& value) const override {
612 std::vector<SchemaError> errors;
613 for (const auto& val : validators_) {
614 if (auto se = vc.validate(val.get(), value)) {
615 errors.emplace_back(*se);
618 const int success = validators_.size() - errors.size();
620 return makeError("at least one valid schema", value);
621 } else if (success > 1 && type_ == Type::EXACTLY_ONE) {
622 return makeError("exactly one valid schema", value);
628 std::vector<std::unique_ptr<IValidator>> validators_;
631 struct RefValidator final : IValidator {
632 explicit RefValidator(IValidator* validator) : validator_(validator) {}
634 Optional<SchemaError> validate(ValidationContext& vc,
635 const dynamic& value) const override {
636 return vc.validate(validator_, value);
638 IValidator* validator_;
641 struct NotValidator final : IValidator {
642 NotValidator(SchemaValidatorContext& context, const dynamic& schema)
643 : validator_(SchemaValidator::make(context, schema)) {}
645 Optional<SchemaError> validate(ValidationContext& vc,
646 const dynamic& value) const override {
647 if (vc.validate(validator_.get(), value)) {
650 return makeError("Expected schema validation to fail", value);
652 std::unique_ptr<IValidator> validator_;
655 void SchemaValidator::loadSchema(SchemaValidatorContext& context,
656 const dynamic& schema) {
657 if (!schema.isObject() || schema.empty()) {
661 // Check for $ref, if we have one we won't apply anything else. Refs are
662 // pointers to other parts of the json, e.g. #/foo/bar points to the schema
663 // located at root["foo"]["bar"].
664 if (const auto* p = schema.get_ptr("$ref")) {
665 // We only support absolute refs, i.e. those starting with '#'
666 if (p->isString() && p->stringPiece()[0] == '#') {
667 auto it = context.refs.find(p->getString());
668 if (it != context.refs.end()) {
669 validators_.emplace_back(make_unique<RefValidator>(it->second));
673 // This is a ref, but we haven't loaded it yet. Find where it is based on
675 std::vector<std::string> parts;
676 split("/", p->stringPiece(), parts);
677 const auto* s = &context.schema; // First part is '#'
678 for (size_t i = 1; s && i < parts.size(); ++i) {
679 // Per the standard, we must replace ~1 with / and then ~0 with ~
680 boost::replace_all(parts[i], "~1", "/");
681 boost::replace_all(parts[i], "~0", "~");
683 s = s->get_ptr(parts[i]);
688 const size_t pos = to<size_t>(parts[i]);
689 if (pos < s->size()) {
693 } catch (const std::range_error& e) {
699 // If you have a self-recursive reference, this avoids getting into an
700 // infinite recursion, where we try to load a schema that just references
701 // itself, and then we try to load it again, and so on.
702 // Instead we load a pointer to the schema into the refs, so that any
703 // future references to it will just see that pointer and won't try to
704 // keep parsing further.
706 auto v = make_unique<SchemaValidator>();
707 context.refs[p->getString()] = v.get();
708 v->loadSchema(context, *s);
709 validators_.emplace_back(std::move(v));
715 // Numeric validators
716 if (const auto* p = schema.get_ptr("multipleOf")) {
717 validators_.emplace_back(make_unique<MultipleOfValidator>(*p));
719 if (const auto* p = schema.get_ptr("maximum")) {
720 validators_.emplace_back(
721 make_unique<ComparisonValidator>(*p,
722 schema.get_ptr("exclusiveMaximum"),
723 ComparisonValidator::Type::MAX));
725 if (const auto* p = schema.get_ptr("minimum")) {
726 validators_.emplace_back(
727 make_unique<ComparisonValidator>(*p,
728 schema.get_ptr("exclusiveMinimum"),
729 ComparisonValidator::Type::MIN));
733 if (const auto* p = schema.get_ptr("maxLength")) {
734 validators_.emplace_back(
735 make_unique<SizeValidator<std::greater_equal<int64_t>>>(
736 *p, dynamic::Type::STRING));
738 if (const auto* p = schema.get_ptr("minLength")) {
739 validators_.emplace_back(
740 make_unique<SizeValidator<std::less_equal<int64_t>>>(
741 *p, dynamic::Type::STRING));
743 if (const auto* p = schema.get_ptr("pattern")) {
744 validators_.emplace_back(make_unique<StringPatternValidator>(*p));
748 const auto* items = schema.get_ptr("items");
749 const auto* additionalItems = schema.get_ptr("additionalItems");
750 if (items || additionalItems) {
751 validators_.emplace_back(
752 make_unique<ArrayItemsValidator>(context, items, additionalItems));
754 if (const auto* p = schema.get_ptr("maxItems")) {
755 validators_.emplace_back(
756 make_unique<SizeValidator<std::greater_equal<int64_t>>>(
757 *p, dynamic::Type::ARRAY));
759 if (const auto* p = schema.get_ptr("minItems")) {
760 validators_.emplace_back(
761 make_unique<SizeValidator<std::less_equal<int64_t>>>(
762 *p, dynamic::Type::ARRAY));
764 if (const auto* p = schema.get_ptr("uniqueItems")) {
765 validators_.emplace_back(make_unique<ArrayUniqueValidator>(*p));
769 const auto* properties = schema.get_ptr("properties");
770 const auto* patternProperties = schema.get_ptr("patternProperties");
771 const auto* additionalProperties = schema.get_ptr("additionalProperties");
772 if (properties || patternProperties || additionalProperties) {
773 validators_.emplace_back(make_unique<PropertiesValidator>(
774 context, properties, patternProperties, additionalProperties));
776 if (const auto* p = schema.get_ptr("maxProperties")) {
777 validators_.emplace_back(
778 make_unique<SizeValidator<std::greater_equal<int64_t>>>(
779 *p, dynamic::Type::OBJECT));
781 if (const auto* p = schema.get_ptr("minProperties")) {
782 validators_.emplace_back(
783 make_unique<SizeValidator<std::less_equal<int64_t>>>(
784 *p, dynamic::Type::OBJECT));
786 if (const auto* p = schema.get_ptr("required")) {
787 validators_.emplace_back(make_unique<RequiredValidator>(*p));
791 if (const auto* p = schema.get_ptr("dependencies")) {
792 validators_.emplace_back(make_unique<DependencyValidator>(context, *p));
794 if (const auto* p = schema.get_ptr("enum")) {
795 validators_.emplace_back(make_unique<EnumValidator>(*p));
797 if (const auto* p = schema.get_ptr("type")) {
798 validators_.emplace_back(make_unique<TypeValidator>(*p));
800 if (const auto* p = schema.get_ptr("allOf")) {
801 validators_.emplace_back(make_unique<AllOfValidator>(context, *p));
803 if (const auto* p = schema.get_ptr("anyOf")) {
804 validators_.emplace_back(make_unique<AnyOfValidator>(
805 context, *p, AnyOfValidator::Type::ONE_OR_MORE));
807 if (const auto* p = schema.get_ptr("oneOf")) {
808 validators_.emplace_back(make_unique<AnyOfValidator>(
809 context, *p, AnyOfValidator::Type::EXACTLY_ONE));
811 if (const auto* p = schema.get_ptr("not")) {
812 validators_.emplace_back(make_unique<NotValidator>(context, *p));
816 void SchemaValidator::validate(const dynamic& value) const {
817 ValidationContext vc;
818 if (auto se = validate(vc, value)) {
823 exception_wrapper SchemaValidator::try_validate(const dynamic& value) const
826 ValidationContext vc;
827 if (auto se = validate(vc, value)) {
828 return make_exception_wrapper<SchemaError>(*se);
830 } catch (const std::exception& e) {
831 return exception_wrapper(std::current_exception(), e);
833 return exception_wrapper(std::current_exception());
835 return exception_wrapper();
838 Optional<SchemaError> SchemaValidator::validate(ValidationContext& vc,
839 const dynamic& value) const {
840 for (const auto& validator : validators_) {
841 if (auto se = vc.validate(validator.get(), value)) {
849 * Metaschema, i.e. schema for schema.
850 * Inlined from the $schema url
852 const char* metaschemaJson =
855 \"id\": \"http://json-schema.org/draft-04/schema#\", \
856 \"$schema\": \"http://json-schema.org/draft-04/schema#\", \
857 \"description\": \"Core schema meta-schema\", \
860 \"type\": \"array\", \
862 \"items\": { \"$ref\": \"#\" } \
864 \"positiveInteger\": { \
865 \"type\": \"integer\", \
868 \"positiveIntegerDefault0\": { \
870 { \"$ref\": \"#/definitions/positiveInteger\" }, { \"default\": 0 } ]\
873 \"enum\": [ \"array\", \"boolean\", \"integer\", \
874 \"null\", \"number\", \"object\", \"string\" ] \
877 \"type\": \"array\", \
878 \"items\": { \"type\": \"string\" }, \
880 \"uniqueItems\": true \
883 \"type\": \"object\", \
886 \"type\": \"string\", \
887 \"format\": \"uri\" \
890 \"type\": \"string\", \
891 \"format\": \"uri\" \
894 \"type\": \"string\" \
897 \"type\": \"string\" \
901 \"type\": \"number\", \
903 \"exclusiveMinimum\": true \
906 \"type\": \"number\" \
908 \"exclusiveMaximum\": { \
909 \"type\": \"boolean\", \
913 \"type\": \"number\" \
915 \"exclusiveMinimum\": { \
916 \"type\": \"boolean\", \
919 \"maxLength\": { \"$ref\": \"#/definitions/positiveInteger\" }, \
920 \"minLength\": { \"$ref\": \"#/definitions/positiveIntegerDefault0\" },\
922 \"type\": \"string\", \
923 \"format\": \"regex\" \
925 \"additionalItems\": { \
927 { \"type\": \"boolean\" }, \
928 { \"$ref\": \"#\" } \
934 { \"$ref\": \"#\" }, \
935 { \"$ref\": \"#/definitions/schemaArray\" } \
939 \"maxItems\": { \"$ref\": \"#/definitions/positiveInteger\" }, \
940 \"minItems\": { \"$ref\": \"#/definitions/positiveIntegerDefault0\" }, \
942 \"type\": \"boolean\", \
945 \"maxProperties\": { \"$ref\": \"#/definitions/positiveInteger\" }, \
946 \"minProperties\": { \
947 \"$ref\": \"#/definitions/positiveIntegerDefault0\" }, \
948 \"required\": { \"$ref\": \"#/definitions/stringArray\" }, \
949 \"additionalProperties\": { \
951 { \"type\": \"boolean\" }, \
952 { \"$ref\": \"#\" } \
957 \"type\": \"object\", \
958 \"additionalProperties\": { \"$ref\": \"#\" }, \
962 \"type\": \"object\", \
963 \"additionalProperties\": { \"$ref\": \"#\" }, \
966 \"patternProperties\": { \
967 \"type\": \"object\", \
968 \"additionalProperties\": { \"$ref\": \"#\" }, \
971 \"dependencies\": { \
972 \"type\": \"object\", \
973 \"additionalProperties\": { \
975 { \"$ref\": \"#\" }, \
976 { \"$ref\": \"#/definitions/stringArray\" } \
981 \"type\": \"array\", \
983 \"uniqueItems\": true \
987 { \"$ref\": \"#/definitions/simpleTypes\" }, \
989 \"type\": \"array\", \
990 \"items\": { \"$ref\": \"#/definitions/simpleTypes\" }, \
992 \"uniqueItems\": true \
996 \"allOf\": { \"$ref\": \"#/definitions/schemaArray\" }, \
997 \"anyOf\": { \"$ref\": \"#/definitions/schemaArray\" }, \
998 \"oneOf\": { \"$ref\": \"#/definitions/schemaArray\" }, \
999 \"not\": { \"$ref\": \"#\" } \
1001 \"dependencies\": { \
1002 \"exclusiveMaximum\": [ \"maximum\" ], \
1003 \"exclusiveMinimum\": [ \"minimum\" ] \
1008 folly::Singleton<Validator> schemaValidator([]() {
1009 return makeValidator(parseJson(metaschemaJson)).release();
1013 Validator::~Validator() = default;
1015 std::unique_ptr<Validator> makeValidator(const dynamic& schema) {
1016 auto v = make_unique<SchemaValidator>();
1017 SchemaValidatorContext context(schema);
1018 context.refs["#"] = v.get();
1019 v->loadSchema(context, schema);
1020 return std::move(v);
1023 Validator* makeSchemaValidator() { return schemaValidator.get(); }