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

Side by Side Diff: chrome/common/json_schema/json_schema_validator.cc

Issue 14830007: Added a validator for JSON v3 schemas. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebased, addressed comments Created 7 years, 7 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 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 "chrome/common/json_schema/json_schema_validator.h" 5 #include "chrome/common/json_schema/json_schema_validator.h"
6 6
7 #include <algorithm>
7 #include <cfloat> 8 #include <cfloat>
8 #include <cmath> 9 #include <cmath>
9 10
11 #include "base/json/json_reader.h"
10 #include "base/string_util.h" 12 #include "base/string_util.h"
13 #include "base/stringprintf.h"
11 #include "base/strings/string_number_conversions.h" 14 #include "base/strings/string_number_conversions.h"
12 #include "base/values.h" 15 #include "base/values.h"
13 #include "chrome/common/json_schema/json_schema_constants.h" 16 #include "chrome/common/json_schema/json_schema_constants.h"
14 #include "ui/base/l10n/l10n_util.h" 17 #include "ui/base/l10n/l10n_util.h"
15 18
16 namespace schema = json_schema_constants; 19 namespace schema = json_schema_constants;
17 20
18 namespace { 21 namespace {
19 22
20 double GetNumberValue(const Value* value) { 23 double GetNumberValue(const Value* value) {
21 double result = 0; 24 double result = 0;
22 CHECK(value->GetAsDouble(&result)) 25 CHECK(value->GetAsDouble(&result))
23 << "Unexpected value type: " << value->GetType(); 26 << "Unexpected value type: " << value->GetType();
24 return result; 27 return result;
25 } 28 }
26 29
30 bool IsValidType(const std::string& type) {
31 static const char* kValidTypes[] = {
32 schema::kAny,
33 schema::kArray,
34 schema::kBoolean,
35 schema::kInteger,
36 schema::kNull,
37 schema::kNumber,
38 schema::kObject,
39 schema::kString,
40 };
41 const char** end = kValidTypes + arraysize(kValidTypes);
42 return std::find(kValidTypes, end, type) != end;
43 }
44
45 // Maps a schema attribute name to its expected type.
46 struct ExpectedType {
47 const char* key;
48 base::Value::Type type;
49 };
50
51 // Helper for std::lower_bound.
52 bool CompareToString(const ExpectedType& entry, const std::string& key) {
53 return entry.key < key;
54 }
55
56 bool IsValidSchema(const base::DictionaryValue* dict, std::string* error) {
57 // This array must be sorted, so that std::lower_bound can perform a
58 // binary search.
59 static const ExpectedType kExpectedTypes[] = {
60 // Note: kRef == "$ref", kSchema == "$schema"
61 { schema::kRef, base::Value::TYPE_STRING },
62 { schema::kSchema, base::Value::TYPE_STRING },
63
64 { schema::kAdditionalProperties, base::Value::TYPE_DICTIONARY },
65 { schema::kChoices, base::Value::TYPE_LIST },
66 { schema::kDescription, base::Value::TYPE_STRING },
67 { schema::kEnum, base::Value::TYPE_LIST },
68 { schema::kId, base::Value::TYPE_STRING },
69 { schema::kMaxItems, base::Value::TYPE_INTEGER },
70 { schema::kMaxLength, base::Value::TYPE_INTEGER },
71 { schema::kMaximum, base::Value::TYPE_DOUBLE },
72 { schema::kMinItems, base::Value::TYPE_INTEGER },
73 { schema::kMinLength, base::Value::TYPE_INTEGER },
74 { schema::kMinimum, base::Value::TYPE_DOUBLE },
75 { schema::kOptional, base::Value::TYPE_BOOLEAN },
76 { schema::kProperties, base::Value::TYPE_DICTIONARY },
77 { schema::kTitle, base::Value::TYPE_STRING },
78 };
79
80 bool has_type = false;
81 const base::ListValue* list_value = NULL;
82 const base::DictionaryValue* dictionary_value = NULL;
83 std::string string_value;
84
85 for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
86 // Validate the "type" attribute, which may be a string or a list.
87 if (it.key() == schema::kType) {
88 switch (it.value().GetType()) {
89 case base::Value::TYPE_STRING:
90 it.value().GetAsString(&string_value);
91 if (!IsValidType(string_value)) {
92 *error = "Invalid value for type attribute";
93 return false;
94 }
95 break;
96 case base::Value::TYPE_LIST:
97 it.value().GetAsList(&list_value);
98 for (size_t i = 0; i < list_value->GetSize(); ++i) {
99 if (!list_value->GetString(i, &string_value) ||
100 !IsValidType(string_value)) {
101 *error = "Invalid value for type attribute";
102 return false;
103 }
104 }
105 break;
106 default:
107 *error = "Invalid value for type attribute";
108 return false;
109 }
110 has_type = true;
111 continue;
112 }
113
114 // Validate the "items" attribute, which is a schema or a list of schemas.
115 if (it.key() == schema::kItems) {
116 if (it.value().GetAsDictionary(&dictionary_value)) {
117 if (!IsValidSchema(dictionary_value, error)) {
118 DCHECK(!error->empty());
119 return false;
120 }
121 } else if (it.value().GetAsList(&list_value)) {
122 for (size_t i = 0; i < list_value->GetSize(); ++i) {
123 if (!list_value->GetDictionary(i, &dictionary_value)) {
124 *error = base::StringPrintf(
125 "Invalid entry in items attribute at index %d",
126 static_cast<int>(i));
not at google - send to devlin 2013/05/20 21:08:52 can you use %u and avoid the static_cast? or does
Joao da Silva 2013/05/21 15:58:28 The problem is that size_t is 32 bits in some plat
127 return false;
128 }
129 if (!IsValidSchema(dictionary_value, error)) {
130 DCHECK(!error->empty());
131 return false;
132 }
133 }
134 } else {
135 *error = "Invalid value for items attribute";
136 return false;
137 }
138 continue;
139 }
140
141 // All the other attributes have a single valid type.
142 const ExpectedType* end = kExpectedTypes + arraysize(kExpectedTypes);
143 const ExpectedType* entry = std::lower_bound(
144 kExpectedTypes, end, it.key(), CompareToString);
145 if (entry == end || entry->key != it.key()) {
146 *error = base::StringPrintf("Invalid attribute %s", it.key().c_str());
147 return false;
148 }
149 if (!it.value().IsType(entry->type)) {
150 *error = base::StringPrintf("Invalid value for %s attribute",
151 it.key().c_str());
152 return false;
153 }
154
155 // base::Value::TYPE_INTEGER attributes must be >= 0.
156 // This applies to "minItems", "maxItems", "minLength" and "maxLength".
157 if (it.value().IsType(base::Value::TYPE_INTEGER)) {
158 int integer_value;
159 it.value().GetAsInteger(&integer_value);
160 if (integer_value < 0) {
161 *error = base::StringPrintf("Value of %s must be >= 0, got %d",
162 it.key().c_str(), integer_value);
163 return false;
164 }
165 }
166
167 // Validate the "properties" attribute. Each entry maps a key to a schema.
168 if (it.key() == schema::kProperties) {
169 it.value().GetAsDictionary(&dictionary_value);
170 for (base::DictionaryValue::Iterator it(*dictionary_value);
171 !it.IsAtEnd(); it.Advance()) {
172 if (!it.value().GetAsDictionary(&dictionary_value)) {
173 *error = "Invalid value for properties attribute";
174 return false;
175 }
176 if (!IsValidSchema(dictionary_value, error)) {
177 DCHECK(!error->empty());
178 return false;
179 }
180 }
181 }
182
183 // Validate "additionalProperties" attribute, which is a schema.
184 if (it.key() == schema::kAdditionalProperties) {
185 it.value().GetAsDictionary(&dictionary_value);
186 if (!IsValidSchema(dictionary_value, error)) {
187 DCHECK(!error->empty());
188 return false;
189 }
190 }
191
192 // Validate the values contained in an "enum" attribute.
193 if (it.key() == schema::kEnum) {
194 it.value().GetAsList(&list_value);
195 for (size_t i = 0; i < list_value->GetSize(); ++i) {
196 const base::Value* value = NULL;
197 list_value->Get(i, &value);
198 switch (value->GetType()) {
199 case base::Value::TYPE_NULL:
200 case base::Value::TYPE_BOOLEAN:
201 case base::Value::TYPE_INTEGER:
202 case base::Value::TYPE_DOUBLE:
203 case base::Value::TYPE_STRING:
204 break;
205 default:
206 *error = "Invalid value in enum attribute";
207 return false;
208 }
209 }
210 }
211
212 // Validate the schemas contained in a "choices" attribute.
213 if (it.key() == schema::kChoices) {
214 it.value().GetAsList(&list_value);
215 for (size_t i = 0; i < list_value->GetSize(); ++i) {
216 if (!list_value->GetDictionary(i, &dictionary_value)) {
217 *error = "Invalid choices attribute";
218 return false;
219 }
220 if (!IsValidSchema(dictionary_value, error)) {
221 DCHECK(!error->empty());
222 return false;
223 }
224 }
225 }
226 }
227
228 if (!has_type) {
229 *error = "Schema must have a type attribute";
230 return false;
231 }
232
233 return true;
234 }
235
27 } // namespace 236 } // namespace
28 237
29 238
30 JSONSchemaValidator::Error::Error() { 239 JSONSchemaValidator::Error::Error() {
31 } 240 }
32 241
33 JSONSchemaValidator::Error::Error(const std::string& message) 242 JSONSchemaValidator::Error::Error(const std::string& message)
34 : path(message) { 243 : path(message) {
35 } 244 }
36 245
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
114 // static 323 // static
115 std::string JSONSchemaValidator::FormatErrorMessage(const std::string& format, 324 std::string JSONSchemaValidator::FormatErrorMessage(const std::string& format,
116 const std::string& s1, 325 const std::string& s1,
117 const std::string& s2) { 326 const std::string& s2) {
118 std::string ret_val = format; 327 std::string ret_val = format;
119 ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s1); 328 ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s1);
120 ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s2); 329 ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s2);
121 return ret_val; 330 return ret_val;
122 } 331 }
123 332
333 // static
334 scoped_ptr<base::DictionaryValue> JSONSchemaValidator::IsValidSchema(
335 const std::string& schema,
336 std::string* error) {
337 base::JSONParserOptions options = base::JSON_PARSE_RFC;
338 scoped_ptr<base::Value> json(
339 base::JSONReader::ReadAndReturnError(schema, options, NULL, error));
340 if (!json)
341 return scoped_ptr<base::DictionaryValue>();
342 base::DictionaryValue* dict = NULL;
343 if (!json->GetAsDictionary(&dict)) {
344 *error = "Schema must be a JSON object";
345 return scoped_ptr<base::DictionaryValue>();
346 }
347 if (!::IsValidSchema(dict, error))
348 return scoped_ptr<base::DictionaryValue>();
349 ignore_result(json.release());
350 return make_scoped_ptr(dict);
351 }
352
124 JSONSchemaValidator::JSONSchemaValidator(DictionaryValue* schema) 353 JSONSchemaValidator::JSONSchemaValidator(DictionaryValue* schema)
125 : schema_root_(schema), default_allow_additional_properties_(false) { 354 : schema_root_(schema), default_allow_additional_properties_(false) {
126 } 355 }
127 356
128 JSONSchemaValidator::JSONSchemaValidator(DictionaryValue* schema, 357 JSONSchemaValidator::JSONSchemaValidator(DictionaryValue* schema,
129 ListValue* types) 358 ListValue* types)
130 : schema_root_(schema), default_allow_additional_properties_(false) { 359 : schema_root_(schema), default_allow_additional_properties_(false) {
131 if (!types) 360 if (!types)
132 return; 361 return;
133 362
(...skipping 353 matching lines...) Expand 10 before | Expand all | Expand 10 after
487 716
488 if (*additional_properties_schema) { 717 if (*additional_properties_schema) {
489 std::string additional_properties_type(schema::kAny); 718 std::string additional_properties_type(schema::kAny);
490 CHECK((*additional_properties_schema)->GetString( 719 CHECK((*additional_properties_schema)->GetString(
491 schema::kType, &additional_properties_type)); 720 schema::kType, &additional_properties_type));
492 return additional_properties_type == schema::kAny; 721 return additional_properties_type == schema::kAny;
493 } else { 722 } else {
494 return default_allow_additional_properties_; 723 return default_allow_additional_properties_;
495 } 724 }
496 } 725 }
OLDNEW
« no previous file with comments | « chrome/common/json_schema/json_schema_validator.h ('k') | chrome/common/json_schema/json_schema_validator_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698