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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: chrome/common/json_schema/json_schema_validator.cc
diff --git a/chrome/common/json_schema/json_schema_validator.cc b/chrome/common/json_schema/json_schema_validator.cc
index 6e5e070b40ddfd6033461171d4f962b1b2ed401e..96d48124354c4152b1213273e7d17723a07b89cd 100644
--- a/chrome/common/json_schema/json_schema_validator.cc
+++ b/chrome/common/json_schema/json_schema_validator.cc
@@ -7,7 +7,9 @@
#include <cfloat>
#include <cmath>
+#include "base/json/json_reader.h"
#include "base/string_util.h"
+#include "base/stringprintf.h"
#include "base/strings/string_number_conversions.h"
#include "base/values.h"
#include "chrome/common/json_schema/json_schema_constants.h"
@@ -24,6 +26,166 @@ double GetNumberValue(const Value* value) {
return result;
}
+bool IsValidType(const std::string& type) {
+ static const char* kValidTypes[] = {
+ schema::kAny,
+ schema::kArray,
+ schema::kBoolean,
+ schema::kInteger,
+ schema::kNull,
+ schema::kNumber,
+ schema::kObject,
+ schema::kString,
+ };
+ const char** end = kValidTypes + arraysize(kValidTypes);
+ return std::find(kValidTypes, end, type) != end;
+}
+
+bool IsValidSchema(const base::DictionaryValue* dict, std::string* error) {
+ static const struct {
+ const char* key;
+ base::Value::Type type;
+ } kExpectedTypes[] = {
+ { schema::kAdditionalProperties, base::Value::TYPE_DICTIONARY },
+ { schema::kProperties, base::Value::TYPE_DICTIONARY },
+
+ { schema::kMinimum, base::Value::TYPE_DOUBLE },
+ { schema::kMaximum, base::Value::TYPE_DOUBLE },
+
+ // All of these must be >= 0.
+ { schema::kMinItems, base::Value::TYPE_INTEGER },
+ { schema::kMaxItems, base::Value::TYPE_INTEGER },
+ { schema::kMinLength, base::Value::TYPE_INTEGER },
+ { schema::kMaxLength, base::Value::TYPE_INTEGER },
+
+ { schema::kEnum, base::Value::TYPE_LIST },
+ { schema::kChoices, base::Value::TYPE_LIST },
+
+ { schema::kId, base::Value::TYPE_STRING },
+ { schema::kRef, base::Value::TYPE_STRING },
+ { schema::kSchema, base::Value::TYPE_STRING },
+
+ { schema::kOptional, base::Value::TYPE_BOOLEAN },
+ };
+
+ const base::Value* value = NULL;
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kExpectedTypes); ++i) {
+ if (!dict->Get(kExpectedTypes[i].key, &value))
+ 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
+
+ if (!value->IsType(kExpectedTypes[i].type)) {
+ *error = base::StringPrintf("Invalid value for %s attribute",
+ kExpectedTypes[i].key);
+ return false;
+ }
+
+ // This applies to "minItems", "maxItems", "minLength" and "maxLength".
+ int integer_value;
+ if (value->GetAsInteger(&integer_value) && integer_value < 0) {
+ *error =
+ 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.
+ return false;
+ }
+ }
+
+ // Validate "type" attribute.
+ std::string string_value;
+ const base::ListValue* list_value = NULL;
+ if (!dict->Get(schema::kType, &value)) {
+ *error = "Schema must have a type attribute";
+ return false;
+ } 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.
+ if (!IsValidType(string_value)) {
+ *error = "Invalid value for type attribute";
+ return false;
+ }
+ } 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.
+ for (size_t i = 0; i < list_value->GetSize(); ++i) {
+ if (!list_value->GetString(i, &string_value) ||
+ !IsValidType(string_value)) {
+ *error = "Invalid value for type attribute";
+ return false;
+ }
+ }
+ } else {
+ *error = "Invalid value for type attribute";
+ return false;
+ }
+
+ // Validate "properties" attribute. Each entry maps a key to a schema.
+ const base::DictionaryValue* dictionary_value = NULL;
+ if (dict->GetDictionary(schema::kProperties, &dictionary_value)) {
+ for (base::DictionaryValue::Iterator it(*dictionary_value);
+ !it.IsAtEnd(); it.Advance()) {
+ if (!it.value().GetAsDictionary(&dictionary_value) ||
+ !IsValidSchema(dictionary_value, error)) {
+ 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
+ }
+ }
+ }
+
+ // Validate "additionalProperties" attribute, which is a schema.
+ if (dict->GetDictionary(schema::kAdditionalProperties, &dictionary_value) &&
+ !IsValidSchema(dictionary_value, error)) {
+ return false;
+ }
+
+ // Validate "items" attribute, which is a schema or a list of schemas.
+ if (dict->Get(schema::kItems, &value)) {
+ if (value->GetAsDictionary(&dictionary_value)) {
+ if (!IsValidSchema(dictionary_value, error))
+ return false;
+ } else if (value->GetAsList(&list_value)) {
+ for (size_t i = 0; i < list_value->GetSize(); ++i) {
+ if (!list_value->GetDictionary(i, &dictionary_value) ||
+ !IsValidSchema(dictionary_value, error)) {
+ return false;
+ }
+ }
+ } else {
+ *error = "Invalid value for items attribute";
+ return false;
+ }
+ }
+
+ // Validate the values contained in an "enum" attribute.
+ if (dict->GetList(schema::kEnum, &list_value)) {
+ for (size_t i = 0; i < list_value->GetSize(); ++i) {
+ list_value->Get(i, &value);
+ switch (value->GetType()) {
+ case base::Value::TYPE_NULL: case base::Value::TYPE_BOOLEAN:
+ case base::Value::TYPE_STRING: case base::Value::TYPE_INTEGER:
+ 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.
+ break;
+ default:
+ *error = "Invalid value in enum attribute";
+ return false;
+ }
+ }
+ }
+
+ // Validate the schemas contained in a "choices" attribute.
+ if (dict->GetList(schema::kChoices, &list_value)) {
+ for (size_t i = 0; i < list_value->GetSize(); ++i) {
+ if (!list_value->GetDictionary(i, &dictionary_value)) {
+ *error = "Invalid choices attribute";
+ return false;
+ } 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.
+ return false;
+ }
+ }
+ }
+
+
+ // 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.
+ if (dict->HasKey(schema::kPattern)) {
+ *error = "pattern attribute is not supported";
+ return false;
+ }
+
+ return true;
+}
+
} // namespace
@@ -121,6 +283,26 @@ std::string JSONSchemaValidator::FormatErrorMessage(const std::string& format,
return ret_val;
}
+// static
+scoped_ptr<base::DictionaryValue> JSONSchemaValidator::IsValidSchema(
+ const std::string& schema,
+ std::string* error) {
+ base::JSONParserOptions options = base::JSON_PARSE_RFC;
+ scoped_ptr<base::Value> json(
+ base::JSONReader::ReadAndReturnError(schema, options, NULL, error));
+ if (!json)
+ return scoped_ptr<base::DictionaryValue>();
+ base::DictionaryValue* dict = NULL;
+ if (!json->GetAsDictionary(&dict)) {
+ *error = "Schema must be a JSON object";
+ return scoped_ptr<base::DictionaryValue>();
+ }
+ if (!::IsValidSchema(dict, error))
+ return scoped_ptr<base::DictionaryValue>();
+ ignore_result(json.release());
+ 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.
+}
+
JSONSchemaValidator::JSONSchemaValidator(DictionaryValue* schema)
: schema_root_(schema), default_allow_additional_properties_(false) {
}

Powered by Google App Engine
This is Rietveld 408576698