Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(47)

Side by Side Diff: components/json_schema/json_schema_validator.cc

Issue 195193002: Add regular expression support in json_schema component (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fix comment Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/json_schema/json_schema_validator.h" 5 #include "components/json_schema/json_schema_validator.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <cfloat> 8 #include <cfloat>
9 #include <cmath> 9 #include <cmath>
10 #include <vector>
10 11
11 #include "base/json/json_reader.h" 12 #include "base/json/json_reader.h"
13 #include "base/logging.h"
14 #include "base/memory/scoped_vector.h"
12 #include "base/strings/string_number_conversions.h" 15 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_util.h" 16 #include "base/strings/string_util.h"
14 #include "base/strings/stringprintf.h" 17 #include "base/strings/stringprintf.h"
15 #include "base/values.h" 18 #include "base/values.h"
16 #include "components/json_schema/json_schema_constants.h" 19 #include "components/json_schema/json_schema_constants.h"
20 #include "third_party/re2/re2/re2.h"
17 21
18 namespace schema = json_schema_constants; 22 namespace schema = json_schema_constants;
19 23
20 namespace { 24 namespace {
21 25
22 double GetNumberValue(const base::Value* value) { 26 double GetNumberValue(const base::Value* value) {
23 double result = 0; 27 double result = 0;
24 CHECK(value->GetAsDouble(&result)) 28 CHECK(value->GetAsDouble(&result))
25 << "Unexpected value type: " << value->GetType(); 29 << "Unexpected value type: " << value->GetType();
26 return result; 30 return result;
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
79 { schema::kDescription, base::Value::TYPE_STRING }, 83 { schema::kDescription, base::Value::TYPE_STRING },
80 { schema::kEnum, base::Value::TYPE_LIST }, 84 { schema::kEnum, base::Value::TYPE_LIST },
81 { schema::kId, base::Value::TYPE_STRING }, 85 { schema::kId, base::Value::TYPE_STRING },
82 { schema::kMaxItems, base::Value::TYPE_INTEGER }, 86 { schema::kMaxItems, base::Value::TYPE_INTEGER },
83 { schema::kMaxLength, base::Value::TYPE_INTEGER }, 87 { schema::kMaxLength, base::Value::TYPE_INTEGER },
84 { schema::kMaximum, base::Value::TYPE_DOUBLE }, 88 { schema::kMaximum, base::Value::TYPE_DOUBLE },
85 { schema::kMinItems, base::Value::TYPE_INTEGER }, 89 { schema::kMinItems, base::Value::TYPE_INTEGER },
86 { schema::kMinLength, base::Value::TYPE_INTEGER }, 90 { schema::kMinLength, base::Value::TYPE_INTEGER },
87 { schema::kMinimum, base::Value::TYPE_DOUBLE }, 91 { schema::kMinimum, base::Value::TYPE_DOUBLE },
88 { schema::kOptional, base::Value::TYPE_BOOLEAN }, 92 { schema::kOptional, base::Value::TYPE_BOOLEAN },
93 { schema::kPattern, base::Value::TYPE_STRING },
94 { schema::kPatternProperties, base::Value::TYPE_DICTIONARY },
89 { schema::kProperties, base::Value::TYPE_DICTIONARY }, 95 { schema::kProperties, base::Value::TYPE_DICTIONARY },
90 { schema::kTitle, base::Value::TYPE_STRING }, 96 { schema::kTitle, base::Value::TYPE_STRING },
91 }; 97 };
92 98
93 bool has_type_or_ref = false; 99 bool has_type_or_ref = false;
94 const base::ListValue* list_value = NULL; 100 const base::ListValue* list_value = NULL;
95 const base::DictionaryValue* dictionary_value = NULL; 101 const base::DictionaryValue* dictionary_value = NULL;
96 std::string string_value; 102 std::string string_value;
97 103
98 for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) { 104 for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
179 if (integer_value < 0) { 185 if (integer_value < 0) {
180 *error = base::StringPrintf("Value of %s must be >= 0, got %d", 186 *error = base::StringPrintf("Value of %s must be >= 0, got %d",
181 it.key().c_str(), integer_value); 187 it.key().c_str(), integer_value);
182 return false; 188 return false;
183 } 189 }
184 } 190 }
185 191
186 // Validate the "properties" attribute. Each entry maps a key to a schema. 192 // Validate the "properties" attribute. Each entry maps a key to a schema.
187 if (it.key() == schema::kProperties) { 193 if (it.key() == schema::kProperties) {
188 it.value().GetAsDictionary(&dictionary_value); 194 it.value().GetAsDictionary(&dictionary_value);
189 for (base::DictionaryValue::Iterator it(*dictionary_value); 195 for (base::DictionaryValue::Iterator iter(*dictionary_value);
190 !it.IsAtEnd(); it.Advance()) { 196 !iter.IsAtEnd(); iter.Advance()) {
191 if (!it.value().GetAsDictionary(&dictionary_value)) { 197 if (!iter.value().GetAsDictionary(&dictionary_value)) {
192 *error = "Invalid value for properties attribute"; 198 *error = "properties must be a dictionary";
193 return false; 199 return false;
194 } 200 }
195 if (!IsValidSchema(dictionary_value, options, error)) { 201 if (!IsValidSchema(dictionary_value, options, error)) {
202 DCHECK(!error->empty());
203 return false;
204 }
205 }
206 }
207
208 // Validate the "patternProperties" attribute. Each entry maps a regular
209 // expression to a schema. The validity of the regular expression expression
210 // won't be checked here for performance reasons. Instead, invalid regular
211 // expressions will be caught as validation errors in Validate().
212 if (it.key() == schema::kPatternProperties) {
213 it.value().GetAsDictionary(&dictionary_value);
214 for (base::DictionaryValue::Iterator iter(*dictionary_value);
215 !iter.IsAtEnd(); iter.Advance()) {
216 if (!iter.value().GetAsDictionary(&dictionary_value)) {
217 *error = "patternProperties must be a dictionary";
218 return false;
219 }
220 if (!IsValidSchema(dictionary_value, options, error)) {
196 DCHECK(!error->empty()); 221 DCHECK(!error->empty());
197 return false; 222 return false;
198 } 223 }
199 } 224 }
200 } 225 }
201 226
202 // Validate "additionalProperties" attribute, which is a schema. 227 // Validate "additionalProperties" attribute, which is a schema.
203 if (it.key() == schema::kAdditionalProperties) { 228 if (it.key() == schema::kAdditionalProperties) {
204 it.value().GetAsDictionary(&dictionary_value); 229 it.value().GetAsDictionary(&dictionary_value);
205 if (!IsValidSchema(dictionary_value, options, error)) { 230 if (!IsValidSchema(dictionary_value, options, error)) {
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after
301 const char JSONSchemaValidator::kStringPattern[] = 326 const char JSONSchemaValidator::kStringPattern[] =
302 "String must match the pattern: *."; 327 "String must match the pattern: *.";
303 const char JSONSchemaValidator::kNumberMinimum[] = 328 const char JSONSchemaValidator::kNumberMinimum[] =
304 "Value must not be less than *."; 329 "Value must not be less than *.";
305 const char JSONSchemaValidator::kNumberMaximum[] = 330 const char JSONSchemaValidator::kNumberMaximum[] =
306 "Value must not be greater than *."; 331 "Value must not be greater than *.";
307 const char JSONSchemaValidator::kInvalidType[] = 332 const char JSONSchemaValidator::kInvalidType[] =
308 "Expected '*' but got '*'."; 333 "Expected '*' but got '*'.";
309 const char JSONSchemaValidator::kInvalidTypeIntegerNumber[] = 334 const char JSONSchemaValidator::kInvalidTypeIntegerNumber[] =
310 "Expected 'integer' but got 'number', consider using Math.round()."; 335 "Expected 'integer' but got 'number', consider using Math.round().";
336 const char JSONSchemaValidator::kInvalidRegex[] =
337 "Regular expression /*/ is invalid: *";
311 338
312 339
313 // static 340 // static
314 std::string JSONSchemaValidator::GetJSONSchemaType(const base::Value* value) { 341 std::string JSONSchemaValidator::GetJSONSchemaType(const base::Value* value) {
315 switch (value->GetType()) { 342 switch (value->GetType()) {
316 case base::Value::TYPE_NULL: 343 case base::Value::TYPE_NULL:
317 return schema::kNull; 344 return schema::kNull;
318 case base::Value::TYPE_BOOLEAN: 345 case base::Value::TYPE_BOOLEAN:
319 return schema::kBoolean; 346 return schema::kBoolean;
320 case base::Value::TYPE_INTEGER: 347 case base::Value::TYPE_INTEGER:
(...skipping 223 matching lines...) Expand 10 before | Expand all | Expand 10 after
544 } 571 }
545 } 572 }
546 573
547 errors_.push_back(Error(path, kInvalidEnum)); 574 errors_.push_back(Error(path, kInvalidEnum));
548 } 575 }
549 576
550 void JSONSchemaValidator::ValidateObject(const base::DictionaryValue* instance, 577 void JSONSchemaValidator::ValidateObject(const base::DictionaryValue* instance,
551 const base::DictionaryValue* schema, 578 const base::DictionaryValue* schema,
552 const std::string& path) { 579 const std::string& path) {
553 const base::DictionaryValue* properties = NULL; 580 const base::DictionaryValue* properties = NULL;
554 schema->GetDictionary(schema::kProperties, &properties); 581 if (schema->GetDictionary(schema::kProperties, &properties)) {
555 if (properties) {
556 for (base::DictionaryValue::Iterator it(*properties); !it.IsAtEnd(); 582 for (base::DictionaryValue::Iterator it(*properties); !it.IsAtEnd();
557 it.Advance()) { 583 it.Advance()) {
558 std::string prop_path = path.empty() ? it.key() : (path + "." + it.key()); 584 std::string prop_path = path.empty() ? it.key() : (path + "." + it.key());
559 const base::DictionaryValue* prop_schema = NULL; 585 const base::DictionaryValue* prop_schema = NULL;
560 CHECK(it.value().GetAsDictionary(&prop_schema)); 586 CHECK(it.value().GetAsDictionary(&prop_schema));
561 587
562 const base::Value* prop_value = NULL; 588 const base::Value* prop_value = NULL;
563 if (instance->Get(it.key(), &prop_value)) { 589 if (instance->Get(it.key(), &prop_value)) {
564 Validate(prop_value, prop_schema, prop_path); 590 Validate(prop_value, prop_schema, prop_path);
565 } else { 591 } else {
566 // Properties are required unless there is an optional field set to 592 // Properties are required unless there is an optional field set to
567 // 'true'. 593 // 'true'.
568 bool is_optional = false; 594 bool is_optional = false;
569 prop_schema->GetBoolean(schema::kOptional, &is_optional); 595 prop_schema->GetBoolean(schema::kOptional, &is_optional);
570 if (!is_optional) { 596 if (!is_optional) {
571 errors_.push_back(Error(prop_path, kObjectPropertyIsRequired)); 597 errors_.push_back(Error(prop_path, kObjectPropertyIsRequired));
572 } 598 }
573 } 599 }
574 } 600 }
575 } 601 }
576 602
577 const base::DictionaryValue* additional_properties_schema = NULL; 603 const base::DictionaryValue* additional_properties_schema = NULL;
578 if (SchemaAllowsAnyAdditionalItems(schema, &additional_properties_schema)) 604 bool allow_any_additional_properties =
579 return; 605 SchemaAllowsAnyAdditionalItems(schema, &additional_properties_schema);
580 606
581 // Validate additional properties. 607 const base::DictionaryValue* pattern_properties = NULL;
608 ScopedVector<re2::RE2> pattern_properties_pattern;
609 std::vector<const base::DictionaryValue*> pattern_properties_schema;
610
611 if (schema->GetDictionary(schema::kPatternProperties, &pattern_properties)) {
612 for (base::DictionaryValue::Iterator it(*pattern_properties); !it.IsAtEnd();
613 it.Advance()) {
614 re2::RE2* prop_pattern = new re2::RE2(it.key());
615 if (!prop_pattern->ok()) {
616 LOG(WARNING) << "Regular expression /" << it.key()
617 << "/ is invalid: " << prop_pattern->error() << ".";
618 errors_.push_back(
619 Error(path,
620 FormatErrorMessage(
621 kInvalidRegex, it.key(), prop_pattern->error())));
622 continue;
623 }
624 const base::DictionaryValue* prop_schema = NULL;
625 CHECK(it.value().GetAsDictionary(&prop_schema));
626 pattern_properties_pattern.push_back(prop_pattern);
627 pattern_properties_schema.push_back(prop_schema);
628 }
629 }
630
631 // Validate pattern properties and additional properties.
582 for (base::DictionaryValue::Iterator it(*instance); !it.IsAtEnd(); 632 for (base::DictionaryValue::Iterator it(*instance); !it.IsAtEnd();
583 it.Advance()) { 633 it.Advance()) {
584 if (properties && properties->HasKey(it.key())) 634 std::string prop_path = path.empty() ? it.key() : path + "." + it.key();
635
636 bool found_matching_pattern = false;
637 for (size_t index = 0; index < pattern_properties_pattern.size(); ++index) {
638 if (re2::RE2::PartialMatch(it.key(),
639 *pattern_properties_pattern[index])) {
640 found_matching_pattern = true;
641 Validate(&it.value(), pattern_properties_schema[index], prop_path);
642 break;
643 }
644 }
645
646 if (found_matching_pattern || allow_any_additional_properties ||
647 (properties && properties->HasKey(it.key())))
585 continue; 648 continue;
586 649
587 std::string prop_path = path.empty() ? it.key() : path + "." + it.key();
588 if (!additional_properties_schema) { 650 if (!additional_properties_schema) {
589 errors_.push_back(Error(prop_path, kUnexpectedProperty)); 651 errors_.push_back(Error(prop_path, kUnexpectedProperty));
590 } else { 652 } else {
591 Validate(&it.value(), additional_properties_schema, prop_path); 653 Validate(&it.value(), additional_properties_schema, prop_path);
592 } 654 }
593 } 655 }
594 } 656 }
595 657
596 void JSONSchemaValidator::ValidateArray(const base::ListValue* instance, 658 void JSONSchemaValidator::ValidateArray(const base::ListValue* instance,
597 const base::DictionaryValue* schema, 659 const base::DictionaryValue* schema,
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after
700 762
701 int max_length = 0; 763 int max_length = 0;
702 if (schema->GetInteger(schema::kMaxLength, &max_length)) { 764 if (schema->GetInteger(schema::kMaxLength, &max_length)) {
703 CHECK(max_length >= 0); 765 CHECK(max_length >= 0);
704 if (value.size() > static_cast<size_t>(max_length)) { 766 if (value.size() > static_cast<size_t>(max_length)) {
705 errors_.push_back(Error(path, FormatErrorMessage( 767 errors_.push_back(Error(path, FormatErrorMessage(
706 kStringMaxLength, base::IntToString(max_length)))); 768 kStringMaxLength, base::IntToString(max_length))));
707 } 769 }
708 } 770 }
709 771
710 CHECK(!schema->HasKey(schema::kPattern)) << "Pattern is not supported."; 772 std::string pattern;
773 if (schema->GetString(schema::kPattern, &pattern)) {
774 re2::RE2 compiled_regex(pattern);
775 if (!compiled_regex.ok()) {
776 LOG(WARNING) << "Regular expression /" << pattern
777 << "/ is invalid: " << compiled_regex.error() << ".";
778 errors_.push_back(Error(
779 path,
780 FormatErrorMessage(kInvalidRegex, pattern, compiled_regex.error())));
781 } else if (!re2::RE2::PartialMatch(value, compiled_regex)) {
782 errors_.push_back(
783 Error(path, FormatErrorMessage(kStringPattern, pattern)));
784 }
785 }
711 } 786 }
712 787
713 void JSONSchemaValidator::ValidateNumber(const base::Value* instance, 788 void JSONSchemaValidator::ValidateNumber(const base::Value* instance,
714 const base::DictionaryValue* schema, 789 const base::DictionaryValue* schema,
715 const std::string& path) { 790 const std::string& path) {
716 double value = GetNumberValue(instance); 791 double value = GetNumberValue(instance);
717 792
718 // TODO(aa): It would be good to test that the double is not infinity or nan, 793 // TODO(aa): It would be good to test that the double is not infinity or nan,
719 // but isnan and isinf aren't defined on Windows. 794 // but isnan and isinf aren't defined on Windows.
720 795
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
761 836
762 if (*additional_properties_schema) { 837 if (*additional_properties_schema) {
763 std::string additional_properties_type(schema::kAny); 838 std::string additional_properties_type(schema::kAny);
764 CHECK((*additional_properties_schema)->GetString( 839 CHECK((*additional_properties_schema)->GetString(
765 schema::kType, &additional_properties_type)); 840 schema::kType, &additional_properties_type));
766 return additional_properties_type == schema::kAny; 841 return additional_properties_type == schema::kAny;
767 } else { 842 } else {
768 return default_allow_additional_properties_; 843 return default_allow_additional_properties_;
769 } 844 }
770 } 845 }
OLDNEW
« no previous file with comments | « components/json_schema/json_schema_validator.h ('k') | components/json_schema/json_schema_validator_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698