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

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: two major changes 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 = "property must be a dictionary";
Joao da Silva 2014/03/13 15:10:52 properties
binjin 2014/03/20 15:14:30 Done.
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 valid
209 // regular expression to a schema.
210 if (it.key() == schema::kPatternProperties) {
211 it.value().GetAsDictionary(&dictionary_value);
212 for (base::DictionaryValue::Iterator iter(*dictionary_value);
213 !iter.IsAtEnd(); iter.Advance()) {
214 if (!iter.value().GetAsDictionary(&dictionary_value)) {
215 *error = "patternProperty must be a dictionary";
216 return false;
217 }
218 if (!IsValidSchema(dictionary_value, options, error)) {
196 DCHECK(!error->empty()); 219 DCHECK(!error->empty());
197 return false; 220 return false;
198 } 221 }
199 } 222 }
200 } 223 }
201 224
202 // Validate "additionalProperties" attribute, which is a schema. 225 // Validate "additionalProperties" attribute, which is a schema.
203 if (it.key() == schema::kAdditionalProperties) { 226 if (it.key() == schema::kAdditionalProperties) {
204 it.value().GetAsDictionary(&dictionary_value); 227 it.value().GetAsDictionary(&dictionary_value);
205 if (!IsValidSchema(dictionary_value, options, error)) { 228 if (!IsValidSchema(dictionary_value, options, error)) {
(...skipping 338 matching lines...) Expand 10 before | Expand all | Expand 10 after
544 } 567 }
545 } 568 }
546 569
547 errors_.push_back(Error(path, kInvalidEnum)); 570 errors_.push_back(Error(path, kInvalidEnum));
548 } 571 }
549 572
550 void JSONSchemaValidator::ValidateObject(const base::DictionaryValue* instance, 573 void JSONSchemaValidator::ValidateObject(const base::DictionaryValue* instance,
551 const base::DictionaryValue* schema, 574 const base::DictionaryValue* schema,
552 const std::string& path) { 575 const std::string& path) {
553 const base::DictionaryValue* properties = NULL; 576 const base::DictionaryValue* properties = NULL;
554 schema->GetDictionary(schema::kProperties, &properties); 577 if (schema->GetDictionary(schema::kProperties, &properties)) {
555 if (properties) {
556 for (base::DictionaryValue::Iterator it(*properties); !it.IsAtEnd(); 578 for (base::DictionaryValue::Iterator it(*properties); !it.IsAtEnd();
557 it.Advance()) { 579 it.Advance()) {
558 std::string prop_path = path.empty() ? it.key() : (path + "." + it.key()); 580 std::string prop_path = path.empty() ? it.key() : (path + "." + it.key());
559 const base::DictionaryValue* prop_schema = NULL; 581 const base::DictionaryValue* prop_schema = NULL;
560 CHECK(it.value().GetAsDictionary(&prop_schema)); 582 CHECK(it.value().GetAsDictionary(&prop_schema));
561 583
562 const base::Value* prop_value = NULL; 584 const base::Value* prop_value = NULL;
563 if (instance->Get(it.key(), &prop_value)) { 585 if (instance->Get(it.key(), &prop_value)) {
564 Validate(prop_value, prop_schema, prop_path); 586 Validate(prop_value, prop_schema, prop_path);
565 } else { 587 } else {
566 // Properties are required unless there is an optional field set to 588 // Properties are required unless there is an optional field set to
567 // 'true'. 589 // 'true'.
568 bool is_optional = false; 590 bool is_optional = false;
569 prop_schema->GetBoolean(schema::kOptional, &is_optional); 591 prop_schema->GetBoolean(schema::kOptional, &is_optional);
570 if (!is_optional) { 592 if (!is_optional) {
571 errors_.push_back(Error(prop_path, kObjectPropertyIsRequired)); 593 errors_.push_back(Error(prop_path, kObjectPropertyIsRequired));
572 } 594 }
573 } 595 }
574 } 596 }
575 } 597 }
576 598
599 // Allowing any additional items will ignore pattern properties as well.
577 const base::DictionaryValue* additional_properties_schema = NULL; 600 const base::DictionaryValue* additional_properties_schema = NULL;
578 if (SchemaAllowsAnyAdditionalItems(schema, &additional_properties_schema)) 601 bool allow_any_additional_properties =
579 return; 602 SchemaAllowsAnyAdditionalItems(schema, &additional_properties_schema);
580 603
581 // Validate additional properties. 604 const base::DictionaryValue* pattern_properties = NULL;
605 ScopedVector<re2::RE2> pattern_properties_pattern;
606 std::vector<const base::DictionaryValue*> pattern_properties_schema;
607
608 if (schema->GetDictionary(schema::kPatternProperties, &pattern_properties)) {
609 for (base::DictionaryValue::Iterator it(*pattern_properties); !it.IsAtEnd();
610 it.Advance()) {
611 re2::RE2* prop_pattern = new re2::RE2(it.key());
612 if (!prop_pattern->ok()) {
613 LOG(WARNING) << "Regular expression in schema is not valid: /"
614 << it.key() << "/.";
Joao da Silva 2014/03/13 15:10:52 LOG prop_pattern.error() and push it as an error t
binjin 2014/03/20 15:14:30 Done.
615 }
616 const base::DictionaryValue* prop_schema = NULL;
617 CHECK(it.value().GetAsDictionary(&prop_schema));
618 pattern_properties_pattern.push_back(prop_pattern);
619 pattern_properties_schema.push_back(prop_schema);
620 }
621 }
622
623 // Validate pattern properties and additional properties.
582 for (base::DictionaryValue::Iterator it(*instance); !it.IsAtEnd(); 624 for (base::DictionaryValue::Iterator it(*instance); !it.IsAtEnd();
583 it.Advance()) { 625 it.Advance()) {
584 if (properties && properties->HasKey(it.key())) 626
Joao da Silva 2014/03/13 15:10:52 nit: remove newline
binjin 2014/03/20 15:14:30 Done.
627 std::string prop_path = path.empty() ? it.key() : path + "." + it.key();
628
629 bool found_matching_pattern = false;
630 for (size_t index = 0; index < pattern_properties_pattern.size(); ++index) {
631 if (pattern_properties_pattern[index]->ok() &&
632 re2::RE2::PartialMatch(it.key(),
633 *pattern_properties_pattern[index])) {
634 found_matching_pattern = true;
635 Validate(&it.value(), pattern_properties_schema[index], prop_path);
636 break;
637 }
638 }
639
640 if (found_matching_pattern || allow_any_additional_properties ||
641 (properties && properties->HasKey(it.key())))
585 continue; 642 continue;
586 643
587 std::string prop_path = path.empty() ? it.key() : path + "." + it.key();
588 if (!additional_properties_schema) { 644 if (!additional_properties_schema) {
589 errors_.push_back(Error(prop_path, kUnexpectedProperty)); 645 errors_.push_back(Error(prop_path, kUnexpectedProperty));
590 } else { 646 } else {
591 Validate(&it.value(), additional_properties_schema, prop_path); 647 Validate(&it.value(), additional_properties_schema, prop_path);
592 } 648 }
593 } 649 }
594 } 650 }
595 651
596 void JSONSchemaValidator::ValidateArray(const base::ListValue* instance, 652 void JSONSchemaValidator::ValidateArray(const base::ListValue* instance,
597 const base::DictionaryValue* schema, 653 const base::DictionaryValue* schema,
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after
700 756
701 int max_length = 0; 757 int max_length = 0;
702 if (schema->GetInteger(schema::kMaxLength, &max_length)) { 758 if (schema->GetInteger(schema::kMaxLength, &max_length)) {
703 CHECK(max_length >= 0); 759 CHECK(max_length >= 0);
704 if (value.size() > static_cast<size_t>(max_length)) { 760 if (value.size() > static_cast<size_t>(max_length)) {
705 errors_.push_back(Error(path, FormatErrorMessage( 761 errors_.push_back(Error(path, FormatErrorMessage(
706 kStringMaxLength, base::IntToString(max_length)))); 762 kStringMaxLength, base::IntToString(max_length))));
707 } 763 }
708 } 764 }
709 765
710 CHECK(!schema->HasKey(schema::kPattern)) << "Pattern is not supported."; 766 std::string pattern;
767 if (schema->GetString(schema::kPattern, &pattern)) {
768 re2::RE2 compiled_regex(pattern);
769 if (!compiled_regex.ok()) {
770 LOG(WARNING) << "Regular expression in schema is not valid: /" << pattern
771 << "/.";
Joao da Silva 2014/03/13 15:10:52 This should push an error too. Otherwise a typo wi
Joao da Silva 2014/03/13 15:10:52 Include compiled_regex.error() in the LOG and the
binjin 2014/03/20 15:14:30 Done.
772 } else if (!re2::RE2::PartialMatch(value, compiled_regex)) {
773 errors_.push_back(
774 Error(path, FormatErrorMessage(kStringPattern, pattern)));
775 }
776 }
711 } 777 }
712 778
713 void JSONSchemaValidator::ValidateNumber(const base::Value* instance, 779 void JSONSchemaValidator::ValidateNumber(const base::Value* instance,
714 const base::DictionaryValue* schema, 780 const base::DictionaryValue* schema,
715 const std::string& path) { 781 const std::string& path) {
716 double value = GetNumberValue(instance); 782 double value = GetNumberValue(instance);
717 783
718 // TODO(aa): It would be good to test that the double is not infinity or nan, 784 // 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. 785 // but isnan and isinf aren't defined on Windows.
720 786
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
761 827
762 if (*additional_properties_schema) { 828 if (*additional_properties_schema) {
763 std::string additional_properties_type(schema::kAny); 829 std::string additional_properties_type(schema::kAny);
764 CHECK((*additional_properties_schema)->GetString( 830 CHECK((*additional_properties_schema)->GetString(
765 schema::kType, &additional_properties_type)); 831 schema::kType, &additional_properties_type));
766 return additional_properties_type == schema::kAny; 832 return additional_properties_type == schema::kAny;
767 } else { 833 } else {
768 return default_allow_additional_properties_; 834 return default_allow_additional_properties_;
769 } 835 }
770 } 836 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698