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