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 3 // found in the LICENSE file. 4 5 #ifndef CHROME_COMMON_JSON_SCHEMA_JSON_SCHEMA_VALIDATOR_H_ 6 #define CHROME_COMMON_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 // - string.pattern 36 // 37 // The following properties are not applicable to the interface exposed by 38 // this class: 39 // - options 40 // - readonly 41 // - title 42 // - description 43 // - format 44 // - default 45 // - transient 46 // - hidden 47 // 48 // There are also these departures from the JSON Schema proposal: 49 // - null counts as 'unspecified' for optional values 50 // - added the 'choices' property, to allow specifying a list of possible types 51 // for a value 52 // - by default an "object" typed schema does not allow additional properties. 53 // if present, "additionalProperties" is to be a schema against which all 54 // additional properties will be validated. 55 //============================================================================== 56 class JSONSchemaValidator { 57 public: 58 // Details about a validation error. 59 struct Error { 60 Error(); 61 62 explicit Error(const std::string& message); 63 64 Error(const std::string& path, const std::string& message); 65 66 // The path to the location of the error in the JSON structure. 67 std::string path; 68 69 // An english message describing the error. 70 std::string message; 71 }; 72 73 // Error messages. 74 static const char kUnknownTypeReference[]; 75 static const char kInvalidChoice[]; 76 static const char kInvalidEnum[]; 77 static const char kObjectPropertyIsRequired[]; 78 static const char kUnexpectedProperty[]; 79 static const char kArrayMinItems[]; 80 static const char kArrayMaxItems[]; 81 static const char kArrayItemRequired[]; 82 static const char kStringMinLength[]; 83 static const char kStringMaxLength[]; 84 static const char kStringPattern[]; 85 static const char kNumberMinimum[]; 86 static const char kNumberMaximum[]; 87 static const char kInvalidType[]; 88 static const char kInvalidTypeIntegerNumber[]; 89 90 // Classifies a Value as one of the JSON schema primitive types. 91 static std::string GetJSONSchemaType(const base::Value* value); 92 93 // Utility methods to format error messages. The first method can have one 94 // wildcard represented by '*', which is replaced with s1. The second method 95 // can have two, which are replaced by s1 and s2. 96 static std::string FormatErrorMessage(const std::string& format, 97 const std::string& s1); 98 static std::string FormatErrorMessage(const std::string& format, 99 const std::string& s1, 100 const std::string& s2); 101 102 // Verifies if |schema| is a valid JSON v3 schema. When this validation passes 103 // then |schema| is valid JSON that can be parsed into a DictionaryValue, 104 // and that DictionaryValue can be used to build a JSONSchemaValidator. 105 // Returns the parsed DictionaryValue when |schema| validated, otherwise 106 // returns NULL. In that case, |error| contains an error description. 107 static scoped_ptr<base::DictionaryValue> IsValidSchema( 108 const std::string& schema, 109 std::string* error); 110 111 // Creates a validator for the specified schema. 112 // 113 // NOTE: This constructor assumes that |schema| is well formed and valid. 114 // Errors will result in CHECK at runtime; this constructor should not be used 115 // with untrusted schemas. 116 explicit JSONSchemaValidator(base::DictionaryValue* schema); 117 118 // Creates a validator for the specified schema and user-defined types. Each 119 // type must be a valid JSONSchema type description with an additional "id" 120 // field. Schema objects in |schema| can refer to these types with the "$ref" 121 // property. 122 // 123 // NOTE: This constructor assumes that |schema| and |types| are well-formed 124 // and valid. Errors will result in CHECK at runtime; this constructor should 125 // not be used with untrusted schemas. 126 JSONSchemaValidator(base::DictionaryValue* schema, base::ListValue* types); 127 128 ~JSONSchemaValidator(); 129 130 // Whether the validator allows additional items for objects and lists, beyond 131 // those defined by their schema, by default. 132 // 133 // This setting defaults to false: all items in an instance list or object 134 // must be defined by the corresponding schema. 135 // 136 // This setting can be overridden on individual object and list schemas by 137 // setting the "additionalProperties" field. 138 bool default_allow_additional_properties() const { 139 return default_allow_additional_properties_; 140 } 141 142 void set_default_allow_additional_properties(bool val) { 143 default_allow_additional_properties_ = val; 144 } 145 146 // Returns any errors from the last call to to Validate(). 147 const std::vector<Error>& errors() const { 148 return errors_; 149 } 150 151 // Validates a JSON value. Returns true if the instance is valid, false 152 // otherwise. If false is returned any errors are available from the errors() 153 // getter. 154 bool Validate(const base::Value* instance); 155 156 private: 157 typedef std::map<std::string, const base::DictionaryValue*> TypeMap; 158 159 // Each of the below methods handle a subset of the validation process. The 160 // path paramater is the path to |instance| from the root of the instance tree 161 // and is used in error messages. 162 163 // Validates any instance node against any schema node. This is called for 164 // every node in the instance tree, and it just decides which of the more 165 // detailed methods to call. 166 void Validate(const base::Value* instance, 167 const base::DictionaryValue* schema, 168 const std::string& path); 169 170 // Validates a node against a list of possible schemas. If any one of the 171 // schemas match, the node is valid. 172 void ValidateChoices(const base::Value* instance, 173 const base::ListValue* choices, 174 const std::string& path); 175 176 // Validates a node against a list of exact primitive values, eg 42, "foobar". 177 void ValidateEnum(const base::Value* instance, 178 const base::ListValue* choices, 179 const std::string& path); 180 181 // Validates a JSON object against an object schema node. 182 void ValidateObject(const base::DictionaryValue* instance, 183 const base::DictionaryValue* schema, 184 const std::string& path); 185 186 // Validates a JSON array against an array schema node. 187 void ValidateArray(const base::ListValue* instance, 188 const base::DictionaryValue* schema, 189 const std::string& path); 190 191 // Validates a JSON array against an array schema node configured to be a 192 // tuple. In a tuple, there is one schema node for each item expected in the 193 // array. 194 void ValidateTuple(const base::ListValue* instance, 195 const base::DictionaryValue* schema, 196 const std::string& path); 197 198 // Validate a JSON string against a string schema node. 199 void ValidateString(const base::Value* instance, 200 const base::DictionaryValue* schema, 201 const std::string& path); 202 203 // Validate a JSON number against a number schema node. 204 void ValidateNumber(const base::Value* instance, 205 const base::DictionaryValue* schema, 206 const std::string& path); 207 208 // Validates that the JSON node |instance| has |expected_type|. 209 bool ValidateType(const base::Value* instance, 210 const std::string& expected_type, 211 const std::string& path); 212 213 // Returns true if |schema| will allow additional items of any type. 214 bool SchemaAllowsAnyAdditionalItems( 215 const base::DictionaryValue* schema, 216 const base::DictionaryValue** addition_items_schema); 217 218 // The root schema node. 219 base::DictionaryValue* schema_root_; 220 221 // Map of user-defined name to type. 222 TypeMap types_; 223 224 // Whether we allow additional properties on objects by default. This can be 225 // overridden by the allow_additional_properties flag on an Object schema. 226 bool default_allow_additional_properties_; 227 228 // Errors accumulated since the last call to Validate(). 229 std::vector<Error> errors_; 230 231 232 DISALLOW_COPY_AND_ASSIGN(JSONSchemaValidator); 233 }; 234 235 #endif // CHROME_COMMON_JSON_SCHEMA_JSON_SCHEMA_VALIDATOR_H_ 236