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

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: . 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 <cfloat> 7 #include <cfloat>
8 #include <cmath> 8 #include <cmath>
9 9
10 #include "base/json/json_reader.h"
10 #include "base/string_util.h" 11 #include "base/string_util.h"
12 #include "base/stringprintf.h"
11 #include "base/strings/string_number_conversions.h" 13 #include "base/strings/string_number_conversions.h"
12 #include "base/values.h" 14 #include "base/values.h"
13 #include "chrome/common/json_schema/json_schema_constants.h" 15 #include "chrome/common/json_schema/json_schema_constants.h"
14 #include "ui/base/l10n/l10n_util.h" 16 #include "ui/base/l10n/l10n_util.h"
15 17
16 namespace schema = json_schema_constants; 18 namespace schema = json_schema_constants;
17 19
18 namespace { 20 namespace {
19 21
20 double GetNumberValue(const Value* value) { 22 double GetNumberValue(const Value* value) {
21 double result = 0; 23 double result = 0;
22 CHECK(value->GetAsDouble(&result)) 24 CHECK(value->GetAsDouble(&result))
23 << "Unexpected value type: " << value->GetType(); 25 << "Unexpected value type: " << value->GetType();
24 return result; 26 return result;
25 } 27 }
26 28
29 bool IsValidType(const std::string& type) {
30 static const char* kValidTypes[] = {
31 schema::kAny,
32 schema::kArray,
33 schema::kBoolean,
34 schema::kInteger,
35 schema::kNull,
36 schema::kNumber,
37 schema::kObject,
38 schema::kString,
39 };
40 const char** end = kValidTypes + arraysize(kValidTypes);
41 return std::find(kValidTypes, end, type) != end;
42 }
43
44 bool IsValidSchema(const base::DictionaryValue* dict, std::string* error) {
45 static const struct {
46 const char* key;
47 base::Value::Type type;
48 } kExpectedTypes[] = {
49 { schema::kAdditionalProperties, base::Value::TYPE_DICTIONARY },
50 { schema::kProperties, base::Value::TYPE_DICTIONARY },
51
52 { schema::kMinimum, base::Value::TYPE_DOUBLE },
53 { schema::kMaximum, base::Value::TYPE_DOUBLE },
54
55 // All of these must be >= 0.
56 { schema::kMinItems, base::Value::TYPE_INTEGER },
57 { schema::kMaxItems, base::Value::TYPE_INTEGER },
58 { schema::kMinLength, base::Value::TYPE_INTEGER },
59 { schema::kMaxLength, base::Value::TYPE_INTEGER },
60
61 { schema::kEnum, base::Value::TYPE_LIST },
62 { schema::kChoices, base::Value::TYPE_LIST },
63
64 { schema::kId, base::Value::TYPE_STRING },
65 { schema::kRef, base::Value::TYPE_STRING },
66 { schema::kSchema, base::Value::TYPE_STRING },
67
68 { schema::kOptional, base::Value::TYPE_BOOLEAN },
69 };
70
71 const base::Value* value = NULL;
72 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kExpectedTypes); ++i) {
73 if (!dict->Get(kExpectedTypes[i].key, &value))
74 continue;
not at google - send to devlin 2013/05/16 17:08:20 the problem with this approach is that it won't in
Joao da Silva 2013/05/19 13:16:29 Good idea, done. "type" and "items" is still speci
75
76 if (!value->IsType(kExpectedTypes[i].type)) {
77 *error = base::StringPrintf("Invalid value for %s attribute",
78 kExpectedTypes[i].key);
79 return false;
80 }
81
82 // This applies to "minItems", "maxItems", "minLength" and "maxLength".
83 int integer_value;
84 if (value->GetAsInteger(&integer_value) && integer_value < 0) {
85 *error =
86 base::StringPrintf("Value of %s must be >= 0", kExpectedTypes[i].key);
not at google - send to devlin 2013/05/16 17:08:20 might as well include integer_value in here.
Joao da Silva 2013/05/19 13:16:29 Done.
87 return false;
88 }
89 }
90
91 // Validate "type" attribute.
92 std::string string_value;
93 const base::ListValue* list_value = NULL;
94 if (!dict->Get(schema::kType, &value)) {
95 *error = "Schema must have a type attribute";
96 return false;
97 } else if (value->GetAsString(&string_value)) {
not at google - send to devlin 2013/05/16 17:08:20 else unnecessary after a return.
Joao da Silva 2013/05/19 13:16:29 Done.
98 if (!IsValidType(string_value)) {
99 *error = "Invalid value for type attribute";
100 return false;
101 }
102 } else if (value->GetAsList(&list_value)) {
not at google - send to devlin 2013/05/16 17:08:20 a switch might be nicer
Joao da Silva 2013/05/19 13:16:29 Done.
103 for (size_t i = 0; i < list_value->GetSize(); ++i) {
104 if (!list_value->GetString(i, &string_value) ||
105 !IsValidType(string_value)) {
106 *error = "Invalid value for type attribute";
107 return false;
108 }
109 }
110 } else {
111 *error = "Invalid value for type attribute";
112 return false;
113 }
114
115 // Validate "properties" attribute. Each entry maps a key to a schema.
116 const base::DictionaryValue* dictionary_value = NULL;
117 if (dict->GetDictionary(schema::kProperties, &dictionary_value)) {
118 for (base::DictionaryValue::Iterator it(*dictionary_value);
119 !it.IsAtEnd(); it.Advance()) {
120 if (!it.value().GetAsDictionary(&dictionary_value) ||
121 !IsValidSchema(dictionary_value, error)) {
122 return false;
not at google - send to devlin 2013/05/16 17:08:20 all these times you validate IsValidSchema and the
Joao da Silva 2013/05/19 13:16:29 That's a good idea, and there's a bug here too: if
123 }
124 }
125 }
126
127 // Validate "additionalProperties" attribute, which is a schema.
128 if (dict->GetDictionary(schema::kAdditionalProperties, &dictionary_value) &&
129 !IsValidSchema(dictionary_value, error)) {
130 return false;
131 }
132
133 // Validate "items" attribute, which is a schema or a list of schemas.
134 if (dict->Get(schema::kItems, &value)) {
135 if (value->GetAsDictionary(&dictionary_value)) {
136 if (!IsValidSchema(dictionary_value, error))
137 return false;
138 } else if (value->GetAsList(&list_value)) {
139 for (size_t i = 0; i < list_value->GetSize(); ++i) {
140 if (!list_value->GetDictionary(i, &dictionary_value) ||
141 !IsValidSchema(dictionary_value, error)) {
142 return false;
143 }
144 }
145 } else {
146 *error = "Invalid value for items attribute";
147 return false;
148 }
149 }
150
151 // Validate the values contained in an "enum" attribute.
152 if (dict->GetList(schema::kEnum, &list_value)) {
153 for (size_t i = 0; i < list_value->GetSize(); ++i) {
154 list_value->Get(i, &value);
155 switch (value->GetType()) {
156 case base::Value::TYPE_NULL: case base::Value::TYPE_BOOLEAN:
157 case base::Value::TYPE_STRING: case base::Value::TYPE_INTEGER:
158 case base::Value::TYPE_DOUBLE:
not at google - send to devlin 2013/05/16 17:08:20 nit: 1 case per line, this looks pretty odd.
Joao da Silva 2013/05/19 13:16:29 Done.
159 break;
160 default:
161 *error = "Invalid value in enum attribute";
162 return false;
163 }
164 }
165 }
166
167 // Validate the schemas contained in a "choices" attribute.
168 if (dict->GetList(schema::kChoices, &list_value)) {
169 for (size_t i = 0; i < list_value->GetSize(); ++i) {
170 if (!list_value->GetDictionary(i, &dictionary_value)) {
171 *error = "Invalid choices attribute";
172 return false;
173 } else if (!IsValidSchema(dictionary_value, error)) {
not at google - send to devlin 2013/05/16 17:08:20 else unnecessary after a return
Joao da Silva 2013/05/19 13:16:29 Done.
174 return false;
175 }
176 }
177 }
178
179
180 // Unsupported attributes.
not at google - send to devlin 2013/05/16 17:08:20 if you do the map thing right at the top then this
Joao da Silva 2013/05/19 13:16:29 Yep, done.
181 if (dict->HasKey(schema::kPattern)) {
182 *error = "pattern attribute is not supported";
183 return false;
184 }
185
186 return true;
187 }
188
27 } // namespace 189 } // namespace
28 190
29 191
30 JSONSchemaValidator::Error::Error() { 192 JSONSchemaValidator::Error::Error() {
31 } 193 }
32 194
33 JSONSchemaValidator::Error::Error(const std::string& message) 195 JSONSchemaValidator::Error::Error(const std::string& message)
34 : path(message) { 196 : path(message) {
35 } 197 }
36 198
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
114 // static 276 // static
115 std::string JSONSchemaValidator::FormatErrorMessage(const std::string& format, 277 std::string JSONSchemaValidator::FormatErrorMessage(const std::string& format,
116 const std::string& s1, 278 const std::string& s1,
117 const std::string& s2) { 279 const std::string& s2) {
118 std::string ret_val = format; 280 std::string ret_val = format;
119 ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s1); 281 ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s1);
120 ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s2); 282 ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s2);
121 return ret_val; 283 return ret_val;
122 } 284 }
123 285
286 // static
287 scoped_ptr<base::DictionaryValue> JSONSchemaValidator::IsValidSchema(
288 const std::string& schema,
289 std::string* error) {
290 base::JSONParserOptions options = base::JSON_PARSE_RFC;
291 scoped_ptr<base::Value> json(
292 base::JSONReader::ReadAndReturnError(schema, options, NULL, error));
293 if (!json)
294 return scoped_ptr<base::DictionaryValue>();
295 base::DictionaryValue* dict = NULL;
296 if (!json->GetAsDictionary(&dict)) {
297 *error = "Schema must be a JSON object";
298 return scoped_ptr<base::DictionaryValue>();
299 }
300 if (!::IsValidSchema(dict, error))
301 return scoped_ptr<base::DictionaryValue>();
302 ignore_result(json.release());
303 return make_scoped_ptr(dict);
not at google - send to devlin 2013/05/16 17:08:20 does just return json.PassAs<base::DictionaryValu
Joao da Silva 2013/05/19 13:16:29 It doesn't; PassAs<>() can only do upcasts.
304 }
305
124 JSONSchemaValidator::JSONSchemaValidator(DictionaryValue* schema) 306 JSONSchemaValidator::JSONSchemaValidator(DictionaryValue* schema)
125 : schema_root_(schema), default_allow_additional_properties_(false) { 307 : schema_root_(schema), default_allow_additional_properties_(false) {
126 } 308 }
127 309
128 JSONSchemaValidator::JSONSchemaValidator(DictionaryValue* schema, 310 JSONSchemaValidator::JSONSchemaValidator(DictionaryValue* schema,
129 ListValue* types) 311 ListValue* types)
130 : schema_root_(schema), default_allow_additional_properties_(false) { 312 : schema_root_(schema), default_allow_additional_properties_(false) {
131 if (!types) 313 if (!types)
132 return; 314 return;
133 315
(...skipping 353 matching lines...) Expand 10 before | Expand all | Expand 10 after
487 669
488 if (*additional_properties_schema) { 670 if (*additional_properties_schema) {
489 std::string additional_properties_type(schema::kAny); 671 std::string additional_properties_type(schema::kAny);
490 CHECK((*additional_properties_schema)->GetString( 672 CHECK((*additional_properties_schema)->GetString(
491 schema::kType, &additional_properties_type)); 673 schema::kType, &additional_properties_type));
492 return additional_properties_type == schema::kAny; 674 return additional_properties_type == schema::kAny;
493 } else { 675 } else {
494 return default_allow_additional_properties_; 676 return default_allow_additional_properties_;
495 } 677 }
496 } 678 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698