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