[email protected] | fb1397ce | 2013-08-13 22:55:07 | [diff] [blame^] | 1 | // Copyright 2013 The Chromium Authors. All rights reserved. |
[email protected] | 639e671 | 2010-11-11 22:21:43 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
[email protected] | fb1397ce | 2013-08-13 22:55:07 | [diff] [blame^] | 5 | #ifndef COMPONENTS_JSON_SCHEMA_JSON_SCHEMA_VALIDATOR_H_ |
| 6 | #define COMPONENTS_JSON_SCHEMA_JSON_SCHEMA_VALIDATOR_H_ |
[email protected] | 639e671 | 2010-11-11 22:21:43 | [diff] [blame] | 7 | |
| 8 | #include <map> |
| 9 | #include <string> |
| 10 | #include <vector> |
| 11 | |
| 12 | #include "base/basictypes.h" |
[email protected] | f4c228d | 2013-05-22 09:00:44 | [diff] [blame] | 13 | #include "base/memory/scoped_ptr.h" |
[email protected] | 639e671 | 2010-11-11 22:21:43 | [diff] [blame] | 14 | |
[email protected] | f3a1c64 | 2011-07-12 19:15:03 | [diff] [blame] | 15 | namespace base { |
[email protected] | 639e671 | 2010-11-11 22:21:43 | [diff] [blame] | 16 | class DictionaryValue; |
[email protected] | 639e671 | 2010-11-11 22:21:43 | [diff] [blame] | 17 | class ListValue; |
| 18 | class StringValue; |
| 19 | class Value; |
[email protected] | f3a1c64 | 2011-07-12 19:15:03 | [diff] [blame] | 20 | } |
[email protected] | 639e671 | 2010-11-11 22:21:43 | [diff] [blame] | 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[]; |
[email protected] | 7a3768e | 2013-03-15 11:18:04 | [diff] [blame] | 88 | static const char kInvalidTypeIntegerNumber[]; |
[email protected] | 639e671 | 2010-11-11 22:21:43 | [diff] [blame] | 89 | |
| 90 | // Classifies a Value as one of the JSON schema primitive types. |
[email protected] | a899c0b0 | 2013-01-18 14:43:27 | [diff] [blame] | 91 | static std::string GetJSONSchemaType(const base::Value* value); |
[email protected] | 639e671 | 2010-11-11 22:21:43 | [diff] [blame] | 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 | |
[email protected] | f4c228d | 2013-05-22 09:00:44 | [diff] [blame] | 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 | |
[email protected] | 639e671 | 2010-11-11 22:21:43 | [diff] [blame] | 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. |
[email protected] | f3a1c64 | 2011-07-12 19:15:03 | [diff] [blame] | 116 | explicit JSONSchemaValidator(base::DictionaryValue* schema); |
[email protected] | 639e671 | 2010-11-11 22:21:43 | [diff] [blame] | 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. |
[email protected] | f3a1c64 | 2011-07-12 19:15:03 | [diff] [blame] | 126 | JSONSchemaValidator(base::DictionaryValue* schema, base::ListValue* types); |
[email protected] | 639e671 | 2010-11-11 22:21:43 | [diff] [blame] | 127 | |
[email protected] | aa20e06 | 2010-12-07 23:07:27 | [diff] [blame] | 128 | ~JSONSchemaValidator(); |
| 129 | |
[email protected] | 639e671 | 2010-11-11 22:21:43 | [diff] [blame] | 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. |
[email protected] | a899c0b0 | 2013-01-18 14:43:27 | [diff] [blame] | 154 | bool Validate(const base::Value* instance); |
[email protected] | 639e671 | 2010-11-11 22:21:43 | [diff] [blame] | 155 | |
| 156 | private: |
[email protected] | a899c0b0 | 2013-01-18 14:43:27 | [diff] [blame] | 157 | typedef std::map<std::string, const base::DictionaryValue*> TypeMap; |
[email protected] | 639e671 | 2010-11-11 22:21:43 | [diff] [blame] | 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. |
[email protected] | a899c0b0 | 2013-01-18 14:43:27 | [diff] [blame] | 166 | void Validate(const base::Value* instance, |
| 167 | const base::DictionaryValue* schema, |
[email protected] | 639e671 | 2010-11-11 22:21:43 | [diff] [blame] | 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. |
[email protected] | a899c0b0 | 2013-01-18 14:43:27 | [diff] [blame] | 172 | void ValidateChoices(const base::Value* instance, |
| 173 | const base::ListValue* choices, |
[email protected] | 639e671 | 2010-11-11 22:21:43 | [diff] [blame] | 174 | const std::string& path); |
| 175 | |
| 176 | // Validates a node against a list of exact primitive values, eg 42, "foobar". |
[email protected] | a899c0b0 | 2013-01-18 14:43:27 | [diff] [blame] | 177 | void ValidateEnum(const base::Value* instance, |
| 178 | const base::ListValue* choices, |
[email protected] | 639e671 | 2010-11-11 22:21:43 | [diff] [blame] | 179 | const std::string& path); |
| 180 | |
| 181 | // Validates a JSON object against an object schema node. |
[email protected] | a899c0b0 | 2013-01-18 14:43:27 | [diff] [blame] | 182 | void ValidateObject(const base::DictionaryValue* instance, |
| 183 | const base::DictionaryValue* schema, |
[email protected] | 639e671 | 2010-11-11 22:21:43 | [diff] [blame] | 184 | const std::string& path); |
| 185 | |
| 186 | // Validates a JSON array against an array schema node. |
[email protected] | a899c0b0 | 2013-01-18 14:43:27 | [diff] [blame] | 187 | void ValidateArray(const base::ListValue* instance, |
| 188 | const base::DictionaryValue* schema, |
[email protected] | 639e671 | 2010-11-11 22:21:43 | [diff] [blame] | 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. |
[email protected] | a899c0b0 | 2013-01-18 14:43:27 | [diff] [blame] | 194 | void ValidateTuple(const base::ListValue* instance, |
| 195 | const base::DictionaryValue* schema, |
[email protected] | 639e671 | 2010-11-11 22:21:43 | [diff] [blame] | 196 | const std::string& path); |
| 197 | |
| 198 | // Validate a JSON string against a string schema node. |
[email protected] | d239230 | 2013-01-29 00:56:41 | [diff] [blame] | 199 | void ValidateString(const base::Value* instance, |
[email protected] | a899c0b0 | 2013-01-18 14:43:27 | [diff] [blame] | 200 | const base::DictionaryValue* schema, |
[email protected] | 639e671 | 2010-11-11 22:21:43 | [diff] [blame] | 201 | const std::string& path); |
| 202 | |
| 203 | // Validate a JSON number against a number schema node. |
[email protected] | a899c0b0 | 2013-01-18 14:43:27 | [diff] [blame] | 204 | void ValidateNumber(const base::Value* instance, |
| 205 | const base::DictionaryValue* schema, |
[email protected] | 639e671 | 2010-11-11 22:21:43 | [diff] [blame] | 206 | const std::string& path); |
| 207 | |
| 208 | // Validates that the JSON node |instance| has |expected_type|. |
[email protected] | a899c0b0 | 2013-01-18 14:43:27 | [diff] [blame] | 209 | bool ValidateType(const base::Value* instance, |
| 210 | const std::string& expected_type, |
[email protected] | 639e671 | 2010-11-11 22:21:43 | [diff] [blame] | 211 | const std::string& path); |
| 212 | |
| 213 | // Returns true if |schema| will allow additional items of any type. |
| 214 | bool SchemaAllowsAnyAdditionalItems( |
[email protected] | a899c0b0 | 2013-01-18 14:43:27 | [diff] [blame] | 215 | const base::DictionaryValue* schema, |
| 216 | const base::DictionaryValue** addition_items_schema); |
[email protected] | 639e671 | 2010-11-11 22:21:43 | [diff] [blame] | 217 | |
| 218 | // The root schema node. |
[email protected] | f3a1c64 | 2011-07-12 19:15:03 | [diff] [blame] | 219 | base::DictionaryValue* schema_root_; |
[email protected] | 639e671 | 2010-11-11 22:21:43 | [diff] [blame] | 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 | |
[email protected] | fb1397ce | 2013-08-13 22:55:07 | [diff] [blame^] | 235 | #endif // COMPONENTS_JSON_SCHEMA_JSON_SCHEMA_VALIDATOR_H_ |