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 3 // found in the LICENSE file. 4 5 #ifndef COMPONENTS_JSON_SCHEMA_JSON_SCHEMA_VALIDATOR_H_ 6 #define COMPONENTS_JSON_SCHEMA_JSON_SCHEMA_VALIDATOR_H_ 7 8 #include <map> 9 #include <string> 10 #include <vector> 11 12 #include "base/basictypes.h" 13 #include "base/memory/scoped_ptr.h" 14 15 namespace base { 16 class DictionaryValue; 17 class ListValue; 18 class StringValue; 19 class Value; 20 } 21 22 //============================================================================== 23 // This class implements a subset of JSON Schema. 24 // See: http://www.json.com/json-schema-proposal/ for more details. 25 // 26 // There is also an older JavaScript implementation of the same functionality in 27 // chrome/renderer/resources/json_schema.js. 28 // 29 // The following features of JSON Schema are not implemented: 30 // - requires 31 // - unique 32 // - disallow 33 // - union types (but replaced with 'choices') 34 // - number.maxDecimal 35 // 36 // The following properties are not applicable to the interface exposed by 37 // this class: 38 // - options 39 // - readonly 40 // - title 41 // - description 42 // - format 43 // - default 44 // - transient 45 // - hidden 46 // 47 // There are also these departures from the JSON Schema proposal: 48 // - null counts as 'unspecified' for optional values 49 // - added the 'choices' property, to allow specifying a list of possible types 50 // for a value 51 // - by default an "object" typed schema does not allow additional properties. 52 // if present, "additionalProperties" is to be a schema against which all 53 // additional properties will be validated. 54 // - regular expression supports all syntaxes that re2 accepts. 55 // See https://code.google.com/p/re2/wiki/Syntax for details. 56 //============================================================================== 57 class JSONSchemaValidator { 58 public: 59 // Details about a validation error. 60 struct Error { 61 Error(); 62 63 explicit Error(const std::string& message); 64 65 Error(const std::string& path, const std::string& message); 66 67 // The path to the location of the error in the JSON structure. 68 std::string path; 69 70 // An english message describing the error. 71 std::string message; 72 }; 73 74 enum Options { 75 // Ignore unknown attributes. If this option is not set then unknown 76 // attributes will make the schema validation fail. 77 OPTIONS_IGNORE_UNKNOWN_ATTRIBUTES = 1 << 0, 78 }; 79 80 // Error messages. 81 static const char kUnknownTypeReference[]; 82 static const char kInvalidChoice[]; 83 static const char kInvalidEnum[]; 84 static const char kObjectPropertyIsRequired[]; 85 static const char kUnexpectedProperty[]; 86 static const char kArrayMinItems[]; 87 static const char kArrayMaxItems[]; 88 static const char kArrayItemRequired[]; 89 static const char kStringMinLength[]; 90 static const char kStringMaxLength[]; 91 static const char kStringPattern[]; 92 static const char kNumberMinimum[]; 93 static const char kNumberMaximum[]; 94 static const char kInvalidType[]; 95 static const char kInvalidTypeIntegerNumber[]; 96 static const char kInvalidRegex[]; 97 98 // Classifies a Value as one of the JSON schema primitive types. 99 static std::string GetJSONSchemaType(const base::Value* value); 100 101 // Utility methods to format error messages. The first method can have one 102 // wildcard represented by '*', which is replaced with s1. The second method 103 // can have two, which are replaced by s1 and s2. 104 static std::string FormatErrorMessage(const std::string& format, 105 const std::string& s1); 106 static std::string FormatErrorMessage(const std::string& format, 107 const std::string& s1, 108 const std::string& s2); 109 110 // Verifies if |schema| is a valid JSON v3 schema. When this validation passes 111 // then |schema| is valid JSON that can be parsed into a DictionaryValue, 112 // and that DictionaryValue can be used to build a JSONSchemaValidator. 113 // Returns the parsed DictionaryValue when |schema| validated, otherwise 114 // returns NULL. In that case, |error| contains an error description. 115 // For performance reasons, currently IsValidSchema() won't check the 116 // correctness of regular expressions used in "pattern" and 117 // "patternProperties" and in Validate() invalid regular expression don't 118 // accept any strings. 119 static scoped_ptr<base::DictionaryValue> IsValidSchema( 120 const std::string& schema, 121 std::string* error); 122 123 // Same as above but with |options|, which is a bitwise-OR combination of the 124 // Options above. 125 static scoped_ptr<base::DictionaryValue> IsValidSchema( 126 const std::string& schema, 127 int options, 128 std::string* error); 129 130 // Creates a validator for the specified schema. 131 // 132 // NOTE: This constructor assumes that |schema| is well formed and valid. 133 // Errors will result in CHECK at runtime; this constructor should not be used 134 // with untrusted schemas. 135 explicit JSONSchemaValidator(base::DictionaryValue* schema); 136 137 // Creates a validator for the specified schema and user-defined types. Each 138 // type must be a valid JSONSchema type description with an additional "id" 139 // field. Schema objects in |schema| can refer to these types with the "$ref" 140 // property. 141 // 142 // NOTE: This constructor assumes that |schema| and |types| are well-formed 143 // and valid. Errors will result in CHECK at runtime; this constructor should 144 // not be used with untrusted schemas. 145 JSONSchemaValidator(base::DictionaryValue* schema, base::ListValue* types); 146 147 ~JSONSchemaValidator(); 148 149 // Whether the validator allows additional items for objects and lists, beyond 150 // those defined by their schema, by default. 151 // 152 // This setting defaults to false: all items in an instance list or object 153 // must be defined by the corresponding schema. 154 // 155 // This setting can be overridden on individual object and list schemas by 156 // setting the "additionalProperties" field. default_allow_additional_properties()157 bool default_allow_additional_properties() const { 158 return default_allow_additional_properties_; 159 } 160 set_default_allow_additional_properties(bool val)161 void set_default_allow_additional_properties(bool val) { 162 default_allow_additional_properties_ = val; 163 } 164 165 // Returns any errors from the last call to to Validate(). errors()166 const std::vector<Error>& errors() const { 167 return errors_; 168 } 169 170 // Validates a JSON value. Returns true if the instance is valid, false 171 // otherwise. If false is returned any errors are available from the errors() 172 // getter. 173 bool Validate(const base::Value* instance); 174 175 private: 176 typedef std::map<std::string, const base::DictionaryValue*> TypeMap; 177 178 // Each of the below methods handle a subset of the validation process. The 179 // path paramater is the path to |instance| from the root of the instance tree 180 // and is used in error messages. 181 182 // Validates any instance node against any schema node. This is called for 183 // every node in the instance tree, and it just decides which of the more 184 // detailed methods to call. 185 void Validate(const base::Value* instance, 186 const base::DictionaryValue* schema, 187 const std::string& path); 188 189 // Validates a node against a list of possible schemas. If any one of the 190 // schemas match, the node is valid. 191 void ValidateChoices(const base::Value* instance, 192 const base::ListValue* choices, 193 const std::string& path); 194 195 // Validates a node against a list of exact primitive values, eg 42, "foobar". 196 void ValidateEnum(const base::Value* instance, 197 const base::ListValue* choices, 198 const std::string& path); 199 200 // Validates a JSON object against an object schema node. 201 void ValidateObject(const base::DictionaryValue* instance, 202 const base::DictionaryValue* schema, 203 const std::string& path); 204 205 // Validates a JSON array against an array schema node. 206 void ValidateArray(const base::ListValue* instance, 207 const base::DictionaryValue* schema, 208 const std::string& path); 209 210 // Validates a JSON array against an array schema node configured to be a 211 // tuple. In a tuple, there is one schema node for each item expected in the 212 // array. 213 void ValidateTuple(const base::ListValue* instance, 214 const base::DictionaryValue* schema, 215 const std::string& path); 216 217 // Validate a JSON string against a string schema node. 218 void ValidateString(const base::Value* instance, 219 const base::DictionaryValue* schema, 220 const std::string& path); 221 222 // Validate a JSON number against a number schema node. 223 void ValidateNumber(const base::Value* instance, 224 const base::DictionaryValue* schema, 225 const std::string& path); 226 227 // Validates that the JSON node |instance| has |expected_type|. 228 bool ValidateType(const base::Value* instance, 229 const std::string& expected_type, 230 const std::string& path); 231 232 // Returns true if |schema| will allow additional items of any type. 233 bool SchemaAllowsAnyAdditionalItems( 234 const base::DictionaryValue* schema, 235 const base::DictionaryValue** addition_items_schema); 236 237 // The root schema node. 238 base::DictionaryValue* schema_root_; 239 240 // Map of user-defined name to type. 241 TypeMap types_; 242 243 // Whether we allow additional properties on objects by default. This can be 244 // overridden by the allow_additional_properties flag on an Object schema. 245 bool default_allow_additional_properties_; 246 247 // Errors accumulated since the last call to Validate(). 248 std::vector<Error> errors_; 249 250 251 DISALLOW_COPY_AND_ASSIGN(JSONSchemaValidator); 252 }; 253 254 #endif // COMPONENTS_JSON_SCHEMA_JSON_SCHEMA_VALIDATOR_H_ 255