json_schema_validator.cc revision 1e9bf3e0803691d0a228da41fc608347b6db4340
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#include "components/json_schema/json_schema_validator.h" 6 7#include <algorithm> 8#include <cfloat> 9#include <cmath> 10 11#include "base/json/json_reader.h" 12#include "base/strings/string_number_conversions.h" 13#include "base/strings/string_util.h" 14#include "base/strings/stringprintf.h" 15#include "base/values.h" 16#include "components/json_schema/json_schema_constants.h" 17#include "ui/base/l10n/l10n_util.h" 18 19namespace schema = json_schema_constants; 20 21namespace { 22 23double GetNumberValue(const base::Value* value) { 24 double result = 0; 25 CHECK(value->GetAsDouble(&result)) 26 << "Unexpected value type: " << value->GetType(); 27 return result; 28} 29 30bool IsValidType(const std::string& type) { 31 static const char* kValidTypes[] = { 32 schema::kAny, 33 schema::kArray, 34 schema::kBoolean, 35 schema::kInteger, 36 schema::kNull, 37 schema::kNumber, 38 schema::kObject, 39 schema::kString, 40 }; 41 const char** end = kValidTypes + arraysize(kValidTypes); 42 return std::find(kValidTypes, end, type) != end; 43} 44 45// Maps a schema attribute name to its expected type. 46struct ExpectedType { 47 const char* key; 48 base::Value::Type type; 49}; 50 51// Helper for std::lower_bound. 52bool CompareToString(const ExpectedType& entry, const std::string& key) { 53 return entry.key < key; 54} 55 56// If |value| is a dictionary, returns the "name" attribute of |value| or NULL 57// if |value| does not contain a "name" attribute. Otherwise, returns |value|. 58const base::Value* ExtractNameFromDictionary(const base::Value* value) { 59 const base::DictionaryValue* value_dict = NULL; 60 const base::Value* name_value = NULL; 61 if (value->GetAsDictionary(&value_dict)) { 62 value_dict->Get("name", &name_value); 63 return name_value; 64 } 65 return value; 66} 67 68bool IsValidSchema(const base::DictionaryValue* dict, std::string* error) { 69 // This array must be sorted, so that std::lower_bound can perform a 70 // binary search. 71 static const ExpectedType kExpectedTypes[] = { 72 // Note: kRef == "$ref", kSchema == "$schema" 73 { schema::kRef, base::Value::TYPE_STRING }, 74 { schema::kSchema, base::Value::TYPE_STRING }, 75 76 { schema::kAdditionalProperties, base::Value::TYPE_DICTIONARY }, 77 { schema::kChoices, base::Value::TYPE_LIST }, 78 { schema::kDescription, base::Value::TYPE_STRING }, 79 { schema::kEnum, base::Value::TYPE_LIST }, 80 { schema::kId, base::Value::TYPE_STRING }, 81 { schema::kMaxItems, base::Value::TYPE_INTEGER }, 82 { schema::kMaxLength, base::Value::TYPE_INTEGER }, 83 { schema::kMaximum, base::Value::TYPE_DOUBLE }, 84 { schema::kMinItems, base::Value::TYPE_INTEGER }, 85 { schema::kMinLength, base::Value::TYPE_INTEGER }, 86 { schema::kMinimum, base::Value::TYPE_DOUBLE }, 87 { schema::kOptional, base::Value::TYPE_BOOLEAN }, 88 { schema::kProperties, base::Value::TYPE_DICTIONARY }, 89 { schema::kTitle, base::Value::TYPE_STRING }, 90 }; 91 92 bool has_type = false; 93 const base::ListValue* list_value = NULL; 94 const base::DictionaryValue* dictionary_value = NULL; 95 std::string string_value; 96 97 for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) { 98 // Validate the "type" attribute, which may be a string or a list. 99 if (it.key() == schema::kType) { 100 switch (it.value().GetType()) { 101 case base::Value::TYPE_STRING: 102 it.value().GetAsString(&string_value); 103 if (!IsValidType(string_value)) { 104 *error = "Invalid value for type attribute"; 105 return false; 106 } 107 break; 108 case base::Value::TYPE_LIST: 109 it.value().GetAsList(&list_value); 110 for (size_t i = 0; i < list_value->GetSize(); ++i) { 111 if (!list_value->GetString(i, &string_value) || 112 !IsValidType(string_value)) { 113 *error = "Invalid value for type attribute"; 114 return false; 115 } 116 } 117 break; 118 default: 119 *error = "Invalid value for type attribute"; 120 return false; 121 } 122 has_type = true; 123 continue; 124 } 125 126 // Validate the "items" attribute, which is a schema or a list of schemas. 127 if (it.key() == schema::kItems) { 128 if (it.value().GetAsDictionary(&dictionary_value)) { 129 if (!IsValidSchema(dictionary_value, error)) { 130 DCHECK(!error->empty()); 131 return false; 132 } 133 } else if (it.value().GetAsList(&list_value)) { 134 for (size_t i = 0; i < list_value->GetSize(); ++i) { 135 if (!list_value->GetDictionary(i, &dictionary_value)) { 136 *error = base::StringPrintf( 137 "Invalid entry in items attribute at index %d", 138 static_cast<int>(i)); 139 return false; 140 } 141 if (!IsValidSchema(dictionary_value, error)) { 142 DCHECK(!error->empty()); 143 return false; 144 } 145 } 146 } else { 147 *error = "Invalid value for items attribute"; 148 return false; 149 } 150 continue; 151 } 152 153 // All the other attributes have a single valid type. 154 const ExpectedType* end = kExpectedTypes + arraysize(kExpectedTypes); 155 const ExpectedType* entry = std::lower_bound( 156 kExpectedTypes, end, it.key(), CompareToString); 157 if (entry == end || entry->key != it.key()) { 158 *error = base::StringPrintf("Invalid attribute %s", it.key().c_str()); 159 return false; 160 } 161 if (!it.value().IsType(entry->type)) { 162 *error = base::StringPrintf("Invalid value for %s attribute", 163 it.key().c_str()); 164 return false; 165 } 166 167 // base::Value::TYPE_INTEGER attributes must be >= 0. 168 // This applies to "minItems", "maxItems", "minLength" and "maxLength". 169 if (it.value().IsType(base::Value::TYPE_INTEGER)) { 170 int integer_value; 171 it.value().GetAsInteger(&integer_value); 172 if (integer_value < 0) { 173 *error = base::StringPrintf("Value of %s must be >= 0, got %d", 174 it.key().c_str(), integer_value); 175 return false; 176 } 177 } 178 179 // Validate the "properties" attribute. Each entry maps a key to a schema. 180 if (it.key() == schema::kProperties) { 181 it.value().GetAsDictionary(&dictionary_value); 182 for (base::DictionaryValue::Iterator it(*dictionary_value); 183 !it.IsAtEnd(); it.Advance()) { 184 if (!it.value().GetAsDictionary(&dictionary_value)) { 185 *error = "Invalid value for properties attribute"; 186 return false; 187 } 188 if (!IsValidSchema(dictionary_value, error)) { 189 DCHECK(!error->empty()); 190 return false; 191 } 192 } 193 } 194 195 // Validate "additionalProperties" attribute, which is a schema. 196 if (it.key() == schema::kAdditionalProperties) { 197 it.value().GetAsDictionary(&dictionary_value); 198 if (!IsValidSchema(dictionary_value, error)) { 199 DCHECK(!error->empty()); 200 return false; 201 } 202 } 203 204 // Validate the values contained in an "enum" attribute. 205 if (it.key() == schema::kEnum) { 206 it.value().GetAsList(&list_value); 207 for (size_t i = 0; i < list_value->GetSize(); ++i) { 208 const base::Value* value = NULL; 209 list_value->Get(i, &value); 210 // Sometimes the enum declaration is a dictionary with the enum value 211 // under "name". 212 value = ExtractNameFromDictionary(value); 213 if (!value) { 214 *error = "Invalid value in enum attribute"; 215 return false; 216 } 217 switch (value->GetType()) { 218 case base::Value::TYPE_NULL: 219 case base::Value::TYPE_BOOLEAN: 220 case base::Value::TYPE_INTEGER: 221 case base::Value::TYPE_DOUBLE: 222 case base::Value::TYPE_STRING: 223 break; 224 default: 225 *error = "Invalid value in enum attribute"; 226 return false; 227 } 228 } 229 } 230 231 // Validate the schemas contained in a "choices" attribute. 232 if (it.key() == schema::kChoices) { 233 it.value().GetAsList(&list_value); 234 for (size_t i = 0; i < list_value->GetSize(); ++i) { 235 if (!list_value->GetDictionary(i, &dictionary_value)) { 236 *error = "Invalid choices attribute"; 237 return false; 238 } 239 if (!IsValidSchema(dictionary_value, error)) { 240 DCHECK(!error->empty()); 241 return false; 242 } 243 } 244 } 245 } 246 247 if (!has_type) { 248 *error = "Schema must have a type attribute"; 249 return false; 250 } 251 252 return true; 253} 254 255} // namespace 256 257 258JSONSchemaValidator::Error::Error() { 259} 260 261JSONSchemaValidator::Error::Error(const std::string& message) 262 : path(message) { 263} 264 265JSONSchemaValidator::Error::Error(const std::string& path, 266 const std::string& message) 267 : path(path), message(message) { 268} 269 270 271const char JSONSchemaValidator::kUnknownTypeReference[] = 272 "Unknown schema reference: *."; 273const char JSONSchemaValidator::kInvalidChoice[] = 274 "Value does not match any valid type choices."; 275const char JSONSchemaValidator::kInvalidEnum[] = 276 "Value does not match any valid enum choices."; 277const char JSONSchemaValidator::kObjectPropertyIsRequired[] = 278 "Property is required."; 279const char JSONSchemaValidator::kUnexpectedProperty[] = 280 "Unexpected property."; 281const char JSONSchemaValidator::kArrayMinItems[] = 282 "Array must have at least * items."; 283const char JSONSchemaValidator::kArrayMaxItems[] = 284 "Array must not have more than * items."; 285const char JSONSchemaValidator::kArrayItemRequired[] = 286 "Item is required."; 287const char JSONSchemaValidator::kStringMinLength[] = 288 "String must be at least * characters long."; 289const char JSONSchemaValidator::kStringMaxLength[] = 290 "String must not be more than * characters long."; 291const char JSONSchemaValidator::kStringPattern[] = 292 "String must match the pattern: *."; 293const char JSONSchemaValidator::kNumberMinimum[] = 294 "Value must not be less than *."; 295const char JSONSchemaValidator::kNumberMaximum[] = 296 "Value must not be greater than *."; 297const char JSONSchemaValidator::kInvalidType[] = 298 "Expected '*' but got '*'."; 299const char JSONSchemaValidator::kInvalidTypeIntegerNumber[] = 300 "Expected 'integer' but got 'number', consider using Math.round()."; 301 302 303// static 304std::string JSONSchemaValidator::GetJSONSchemaType(const base::Value* value) { 305 switch (value->GetType()) { 306 case base::Value::TYPE_NULL: 307 return schema::kNull; 308 case base::Value::TYPE_BOOLEAN: 309 return schema::kBoolean; 310 case base::Value::TYPE_INTEGER: 311 return schema::kInteger; 312 case base::Value::TYPE_DOUBLE: { 313 double double_value = 0; 314 value->GetAsDouble(&double_value); 315 if (std::abs(double_value) <= std::pow(2.0, DBL_MANT_DIG) && 316 double_value == floor(double_value)) { 317 return schema::kInteger; 318 } else { 319 return schema::kNumber; 320 } 321 } 322 case base::Value::TYPE_STRING: 323 return schema::kString; 324 case base::Value::TYPE_DICTIONARY: 325 return schema::kObject; 326 case base::Value::TYPE_LIST: 327 return schema::kArray; 328 default: 329 NOTREACHED() << "Unexpected value type: " << value->GetType(); 330 return std::string(); 331 } 332} 333 334// static 335std::string JSONSchemaValidator::FormatErrorMessage(const std::string& format, 336 const std::string& s1) { 337 std::string ret_val = format; 338 ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s1); 339 return ret_val; 340} 341 342// static 343std::string JSONSchemaValidator::FormatErrorMessage(const std::string& format, 344 const std::string& s1, 345 const std::string& s2) { 346 std::string ret_val = format; 347 ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s1); 348 ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s2); 349 return ret_val; 350} 351 352// static 353scoped_ptr<base::DictionaryValue> JSONSchemaValidator::IsValidSchema( 354 const std::string& schema, 355 std::string* error) { 356 base::JSONParserOptions options = base::JSON_PARSE_RFC; 357 scoped_ptr<base::Value> json( 358 base::JSONReader::ReadAndReturnError(schema, options, NULL, error)); 359 if (!json) 360 return scoped_ptr<base::DictionaryValue>(); 361 base::DictionaryValue* dict = NULL; 362 if (!json->GetAsDictionary(&dict)) { 363 *error = "Schema must be a JSON object"; 364 return scoped_ptr<base::DictionaryValue>(); 365 } 366 if (!::IsValidSchema(dict, error)) 367 return scoped_ptr<base::DictionaryValue>(); 368 ignore_result(json.release()); 369 return make_scoped_ptr(dict); 370} 371 372JSONSchemaValidator::JSONSchemaValidator(base::DictionaryValue* schema) 373 : schema_root_(schema), default_allow_additional_properties_(false) { 374} 375 376JSONSchemaValidator::JSONSchemaValidator(base::DictionaryValue* schema, 377 base::ListValue* types) 378 : schema_root_(schema), default_allow_additional_properties_(false) { 379 if (!types) 380 return; 381 382 for (size_t i = 0; i < types->GetSize(); ++i) { 383 base::DictionaryValue* type = NULL; 384 CHECK(types->GetDictionary(i, &type)); 385 386 std::string id; 387 CHECK(type->GetString(schema::kId, &id)); 388 389 CHECK(types_.find(id) == types_.end()); 390 types_[id] = type; 391 } 392} 393 394JSONSchemaValidator::~JSONSchemaValidator() {} 395 396bool JSONSchemaValidator::Validate(const base::Value* instance) { 397 errors_.clear(); 398 Validate(instance, schema_root_, std::string()); 399 return errors_.empty(); 400} 401 402void JSONSchemaValidator::Validate(const base::Value* instance, 403 const base::DictionaryValue* schema, 404 const std::string& path) { 405 // If this schema defines itself as reference type, save it in this.types. 406 std::string id; 407 if (schema->GetString(schema::kId, &id)) { 408 TypeMap::iterator iter = types_.find(id); 409 if (iter == types_.end()) 410 types_[id] = schema; 411 else 412 DCHECK(iter->second == schema); 413 } 414 415 // If the schema has a $ref property, the instance must validate against 416 // that schema. It must be present in types_ to be referenced. 417 std::string ref; 418 if (schema->GetString(schema::kRef, &ref)) { 419 TypeMap::iterator type = types_.find(ref); 420 if (type == types_.end()) { 421 errors_.push_back( 422 Error(path, FormatErrorMessage(kUnknownTypeReference, ref))); 423 } else { 424 Validate(instance, type->second, path); 425 } 426 return; 427 } 428 429 // If the schema has a choices property, the instance must validate against at 430 // least one of the items in that array. 431 const base::ListValue* choices = NULL; 432 if (schema->GetList(schema::kChoices, &choices)) { 433 ValidateChoices(instance, choices, path); 434 return; 435 } 436 437 // If the schema has an enum property, the instance must be one of those 438 // values. 439 const base::ListValue* enumeration = NULL; 440 if (schema->GetList(schema::kEnum, &enumeration)) { 441 ValidateEnum(instance, enumeration, path); 442 return; 443 } 444 445 std::string type; 446 schema->GetString(schema::kType, &type); 447 CHECK(!type.empty()); 448 if (type != schema::kAny) { 449 if (!ValidateType(instance, type, path)) 450 return; 451 452 // These casts are safe because of checks in ValidateType(). 453 if (type == schema::kObject) { 454 ValidateObject(static_cast<const base::DictionaryValue*>(instance), 455 schema, 456 path); 457 } else if (type == schema::kArray) { 458 ValidateArray(static_cast<const base::ListValue*>(instance), 459 schema, path); 460 } else if (type == schema::kString) { 461 // Intentionally NOT downcasting to StringValue*. TYPE_STRING only implies 462 // GetAsString() can safely be carried out, not that it's a StringValue. 463 ValidateString(instance, schema, path); 464 } else if (type == schema::kNumber || type == schema::kInteger) { 465 ValidateNumber(instance, schema, path); 466 } else if (type != schema::kBoolean && type != schema::kNull) { 467 NOTREACHED() << "Unexpected type: " << type; 468 } 469 } 470} 471 472void JSONSchemaValidator::ValidateChoices(const base::Value* instance, 473 const base::ListValue* choices, 474 const std::string& path) { 475 size_t original_num_errors = errors_.size(); 476 477 for (size_t i = 0; i < choices->GetSize(); ++i) { 478 const base::DictionaryValue* choice = NULL; 479 CHECK(choices->GetDictionary(i, &choice)); 480 481 Validate(instance, choice, path); 482 if (errors_.size() == original_num_errors) 483 return; 484 485 // We discard the error from each choice. We only want to know if any of the 486 // validations succeeded. 487 errors_.resize(original_num_errors); 488 } 489 490 // Now add a generic error that no choices matched. 491 errors_.push_back(Error(path, kInvalidChoice)); 492 return; 493} 494 495void JSONSchemaValidator::ValidateEnum(const base::Value* instance, 496 const base::ListValue* choices, 497 const std::string& path) { 498 for (size_t i = 0; i < choices->GetSize(); ++i) { 499 const base::Value* choice = NULL; 500 CHECK(choices->Get(i, &choice)); 501 // Sometimes the enum declaration is a dictionary with the enum value under 502 // "name". 503 choice = ExtractNameFromDictionary(choice); 504 if (!choice) { 505 NOTREACHED(); 506 } 507 switch (choice->GetType()) { 508 case base::Value::TYPE_NULL: 509 case base::Value::TYPE_BOOLEAN: 510 case base::Value::TYPE_STRING: 511 if (instance->Equals(choice)) 512 return; 513 break; 514 515 case base::Value::TYPE_INTEGER: 516 case base::Value::TYPE_DOUBLE: 517 if (instance->IsType(base::Value::TYPE_INTEGER) || 518 instance->IsType(base::Value::TYPE_DOUBLE)) { 519 if (GetNumberValue(choice) == GetNumberValue(instance)) 520 return; 521 } 522 break; 523 524 default: 525 NOTREACHED() << "Unexpected type in enum: " << choice->GetType(); 526 } 527 } 528 529 errors_.push_back(Error(path, kInvalidEnum)); 530} 531 532void JSONSchemaValidator::ValidateObject(const base::DictionaryValue* instance, 533 const base::DictionaryValue* schema, 534 const std::string& path) { 535 const base::DictionaryValue* properties = NULL; 536 schema->GetDictionary(schema::kProperties, &properties); 537 if (properties) { 538 for (base::DictionaryValue::Iterator it(*properties); !it.IsAtEnd(); 539 it.Advance()) { 540 std::string prop_path = path.empty() ? it.key() : (path + "." + it.key()); 541 const base::DictionaryValue* prop_schema = NULL; 542 CHECK(it.value().GetAsDictionary(&prop_schema)); 543 544 const base::Value* prop_value = NULL; 545 if (instance->Get(it.key(), &prop_value)) { 546 Validate(prop_value, prop_schema, prop_path); 547 } else { 548 // Properties are required unless there is an optional field set to 549 // 'true'. 550 bool is_optional = false; 551 prop_schema->GetBoolean(schema::kOptional, &is_optional); 552 if (!is_optional) { 553 errors_.push_back(Error(prop_path, kObjectPropertyIsRequired)); 554 } 555 } 556 } 557 } 558 559 const base::DictionaryValue* additional_properties_schema = NULL; 560 if (SchemaAllowsAnyAdditionalItems(schema, &additional_properties_schema)) 561 return; 562 563 // Validate additional properties. 564 for (base::DictionaryValue::Iterator it(*instance); !it.IsAtEnd(); 565 it.Advance()) { 566 if (properties && properties->HasKey(it.key())) 567 continue; 568 569 std::string prop_path = path.empty() ? it.key() : path + "." + it.key(); 570 if (!additional_properties_schema) { 571 errors_.push_back(Error(prop_path, kUnexpectedProperty)); 572 } else { 573 Validate(&it.value(), additional_properties_schema, prop_path); 574 } 575 } 576} 577 578void JSONSchemaValidator::ValidateArray(const base::ListValue* instance, 579 const base::DictionaryValue* schema, 580 const std::string& path) { 581 const base::DictionaryValue* single_type = NULL; 582 size_t instance_size = instance->GetSize(); 583 if (schema->GetDictionary(schema::kItems, &single_type)) { 584 int min_items = 0; 585 if (schema->GetInteger(schema::kMinItems, &min_items)) { 586 CHECK(min_items >= 0); 587 if (instance_size < static_cast<size_t>(min_items)) { 588 errors_.push_back(Error(path, FormatErrorMessage( 589 kArrayMinItems, base::IntToString(min_items)))); 590 } 591 } 592 593 int max_items = 0; 594 if (schema->GetInteger(schema::kMaxItems, &max_items)) { 595 CHECK(max_items >= 0); 596 if (instance_size > static_cast<size_t>(max_items)) { 597 errors_.push_back(Error(path, FormatErrorMessage( 598 kArrayMaxItems, base::IntToString(max_items)))); 599 } 600 } 601 602 // If the items property is a single schema, each item in the array must 603 // validate against that schema. 604 for (size_t i = 0; i < instance_size; ++i) { 605 const base::Value* item = NULL; 606 CHECK(instance->Get(i, &item)); 607 std::string i_str = base::Uint64ToString(i); 608 std::string item_path = path.empty() ? i_str : (path + "." + i_str); 609 Validate(item, single_type, item_path); 610 } 611 612 return; 613 } 614 615 // Otherwise, the list must be a tuple type, where each item in the list has a 616 // particular schema. 617 ValidateTuple(instance, schema, path); 618} 619 620void JSONSchemaValidator::ValidateTuple(const base::ListValue* instance, 621 const base::DictionaryValue* schema, 622 const std::string& path) { 623 const base::ListValue* tuple_type = NULL; 624 schema->GetList(schema::kItems, &tuple_type); 625 size_t tuple_size = tuple_type ? tuple_type->GetSize() : 0; 626 if (tuple_type) { 627 for (size_t i = 0; i < tuple_size; ++i) { 628 std::string i_str = base::Uint64ToString(i); 629 std::string item_path = path.empty() ? i_str : (path + "." + i_str); 630 const base::DictionaryValue* item_schema = NULL; 631 CHECK(tuple_type->GetDictionary(i, &item_schema)); 632 const base::Value* item_value = NULL; 633 instance->Get(i, &item_value); 634 if (item_value && item_value->GetType() != base::Value::TYPE_NULL) { 635 Validate(item_value, item_schema, item_path); 636 } else { 637 bool is_optional = false; 638 item_schema->GetBoolean(schema::kOptional, &is_optional); 639 if (!is_optional) { 640 errors_.push_back(Error(item_path, kArrayItemRequired)); 641 return; 642 } 643 } 644 } 645 } 646 647 const base::DictionaryValue* additional_properties_schema = NULL; 648 if (SchemaAllowsAnyAdditionalItems(schema, &additional_properties_schema)) 649 return; 650 651 size_t instance_size = instance->GetSize(); 652 if (additional_properties_schema) { 653 // Any additional properties must validate against the additionalProperties 654 // schema. 655 for (size_t i = tuple_size; i < instance_size; ++i) { 656 std::string i_str = base::Uint64ToString(i); 657 std::string item_path = path.empty() ? i_str : (path + "." + i_str); 658 const base::Value* item_value = NULL; 659 CHECK(instance->Get(i, &item_value)); 660 Validate(item_value, additional_properties_schema, item_path); 661 } 662 } else if (instance_size > tuple_size) { 663 errors_.push_back(Error(path, FormatErrorMessage( 664 kArrayMaxItems, base::Uint64ToString(tuple_size)))); 665 } 666} 667 668void JSONSchemaValidator::ValidateString(const base::Value* instance, 669 const base::DictionaryValue* schema, 670 const std::string& path) { 671 std::string value; 672 CHECK(instance->GetAsString(&value)); 673 674 int min_length = 0; 675 if (schema->GetInteger(schema::kMinLength, &min_length)) { 676 CHECK(min_length >= 0); 677 if (value.size() < static_cast<size_t>(min_length)) { 678 errors_.push_back(Error(path, FormatErrorMessage( 679 kStringMinLength, base::IntToString(min_length)))); 680 } 681 } 682 683 int max_length = 0; 684 if (schema->GetInteger(schema::kMaxLength, &max_length)) { 685 CHECK(max_length >= 0); 686 if (value.size() > static_cast<size_t>(max_length)) { 687 errors_.push_back(Error(path, FormatErrorMessage( 688 kStringMaxLength, base::IntToString(max_length)))); 689 } 690 } 691 692 CHECK(!schema->HasKey(schema::kPattern)) << "Pattern is not supported."; 693} 694 695void JSONSchemaValidator::ValidateNumber(const base::Value* instance, 696 const base::DictionaryValue* schema, 697 const std::string& path) { 698 double value = GetNumberValue(instance); 699 700 // TODO(aa): It would be good to test that the double is not infinity or nan, 701 // but isnan and isinf aren't defined on Windows. 702 703 double minimum = 0; 704 if (schema->GetDouble(schema::kMinimum, &minimum)) { 705 if (value < minimum) 706 errors_.push_back(Error(path, FormatErrorMessage( 707 kNumberMinimum, base::DoubleToString(minimum)))); 708 } 709 710 double maximum = 0; 711 if (schema->GetDouble(schema::kMaximum, &maximum)) { 712 if (value > maximum) 713 errors_.push_back(Error(path, FormatErrorMessage( 714 kNumberMaximum, base::DoubleToString(maximum)))); 715 } 716} 717 718bool JSONSchemaValidator::ValidateType(const base::Value* instance, 719 const std::string& expected_type, 720 const std::string& path) { 721 std::string actual_type = GetJSONSchemaType(instance); 722 if (expected_type == actual_type || 723 (expected_type == schema::kNumber && actual_type == schema::kInteger)) { 724 return true; 725 } else if (expected_type == schema::kInteger && 726 actual_type == schema::kNumber) { 727 errors_.push_back(Error(path, kInvalidTypeIntegerNumber)); 728 return false; 729 } else { 730 errors_.push_back(Error(path, FormatErrorMessage( 731 kInvalidType, expected_type, actual_type))); 732 return false; 733 } 734} 735 736bool JSONSchemaValidator::SchemaAllowsAnyAdditionalItems( 737 const base::DictionaryValue* schema, 738 const base::DictionaryValue** additional_properties_schema) { 739 // If the validator allows additional properties globally, and this schema 740 // doesn't override, then we can exit early. 741 schema->GetDictionary(schema::kAdditionalProperties, 742 additional_properties_schema); 743 744 if (*additional_properties_schema) { 745 std::string additional_properties_type(schema::kAny); 746 CHECK((*additional_properties_schema)->GetString( 747 schema::kType, &additional_properties_type)); 748 return additional_properties_type == schema::kAny; 749 } else { 750 return default_allow_additional_properties_; 751 } 752} 753