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.
5 #include "components/json_schema/json_schema_validator.h"
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"
19 namespace schema
= json_schema_constants
;
23 double GetNumberValue(const base::Value
* value
) {
25 CHECK(value
->GetAsDouble(&result
))
26 << "Unexpected value type: " << value
->GetType();
30 bool IsValidType(const std::string
& type
) {
31 static const char* kValidTypes
[] = {
41 const char** end
= kValidTypes
+ arraysize(kValidTypes
);
42 return std::find(kValidTypes
, end
, type
) != end
;
45 // Maps a schema attribute name to its expected type.
48 base::Value::Type type
;
51 // Helper for std::lower_bound.
52 bool CompareToString(const ExpectedType
& entry
, const std::string
& key
) {
53 return entry
.key
< key
;
56 bool IsValidSchema(const base::DictionaryValue
* dict
, std::string
* error
) {
57 // This array must be sorted, so that std::lower_bound can perform a
59 static const ExpectedType kExpectedTypes
[] = {
60 // Note: kRef == "$ref", kSchema == "$schema"
61 { schema::kRef
, base::Value::TYPE_STRING
},
62 { schema::kSchema
, base::Value::TYPE_STRING
},
64 { schema::kAdditionalProperties
, base::Value::TYPE_DICTIONARY
},
65 { schema::kChoices
, base::Value::TYPE_LIST
},
66 { schema::kDescription
, base::Value::TYPE_STRING
},
67 { schema::kEnum
, base::Value::TYPE_LIST
},
68 { schema::kId
, base::Value::TYPE_STRING
},
69 { schema::kMaxItems
, base::Value::TYPE_INTEGER
},
70 { schema::kMaxLength
, base::Value::TYPE_INTEGER
},
71 { schema::kMaximum
, base::Value::TYPE_DOUBLE
},
72 { schema::kMinItems
, base::Value::TYPE_INTEGER
},
73 { schema::kMinLength
, base::Value::TYPE_INTEGER
},
74 { schema::kMinimum
, base::Value::TYPE_DOUBLE
},
75 { schema::kOptional
, base::Value::TYPE_BOOLEAN
},
76 { schema::kProperties
, base::Value::TYPE_DICTIONARY
},
77 { schema::kTitle
, base::Value::TYPE_STRING
},
80 bool has_type
= false;
81 const base::ListValue
* list_value
= NULL
;
82 const base::DictionaryValue
* dictionary_value
= NULL
;
83 std::string string_value
;
85 for (base::DictionaryValue::Iterator
it(*dict
); !it
.IsAtEnd(); it
.Advance()) {
86 // Validate the "type" attribute, which may be a string or a list.
87 if (it
.key() == schema::kType
) {
88 switch (it
.value().GetType()) {
89 case base::Value::TYPE_STRING
:
90 it
.value().GetAsString(&string_value
);
91 if (!IsValidType(string_value
)) {
92 *error
= "Invalid value for type attribute";
96 case base::Value::TYPE_LIST
:
97 it
.value().GetAsList(&list_value
);
98 for (size_t i
= 0; i
< list_value
->GetSize(); ++i
) {
99 if (!list_value
->GetString(i
, &string_value
) ||
100 !IsValidType(string_value
)) {
101 *error
= "Invalid value for type attribute";
107 *error
= "Invalid value for type attribute";
114 // Validate the "items" attribute, which is a schema or a list of schemas.
115 if (it
.key() == schema::kItems
) {
116 if (it
.value().GetAsDictionary(&dictionary_value
)) {
117 if (!IsValidSchema(dictionary_value
, error
)) {
118 DCHECK(!error
->empty());
121 } else if (it
.value().GetAsList(&list_value
)) {
122 for (size_t i
= 0; i
< list_value
->GetSize(); ++i
) {
123 if (!list_value
->GetDictionary(i
, &dictionary_value
)) {
124 *error
= base::StringPrintf(
125 "Invalid entry in items attribute at index %d",
126 static_cast<int>(i
));
129 if (!IsValidSchema(dictionary_value
, error
)) {
130 DCHECK(!error
->empty());
135 *error
= "Invalid value for items attribute";
141 // All the other attributes have a single valid type.
142 const ExpectedType
* end
= kExpectedTypes
+ arraysize(kExpectedTypes
);
143 const ExpectedType
* entry
= std::lower_bound(
144 kExpectedTypes
, end
, it
.key(), CompareToString
);
145 if (entry
== end
|| entry
->key
!= it
.key()) {
146 *error
= base::StringPrintf("Invalid attribute %s", it
.key().c_str());
149 if (!it
.value().IsType(entry
->type
)) {
150 *error
= base::StringPrintf("Invalid value for %s attribute",
155 // base::Value::TYPE_INTEGER attributes must be >= 0.
156 // This applies to "minItems", "maxItems", "minLength" and "maxLength".
157 if (it
.value().IsType(base::Value::TYPE_INTEGER
)) {
159 it
.value().GetAsInteger(&integer_value
);
160 if (integer_value
< 0) {
161 *error
= base::StringPrintf("Value of %s must be >= 0, got %d",
162 it
.key().c_str(), integer_value
);
167 // Validate the "properties" attribute. Each entry maps a key to a schema.
168 if (it
.key() == schema::kProperties
) {
169 it
.value().GetAsDictionary(&dictionary_value
);
170 for (base::DictionaryValue::Iterator
it(*dictionary_value
);
171 !it
.IsAtEnd(); it
.Advance()) {
172 if (!it
.value().GetAsDictionary(&dictionary_value
)) {
173 *error
= "Invalid value for properties attribute";
176 if (!IsValidSchema(dictionary_value
, error
)) {
177 DCHECK(!error
->empty());
183 // Validate "additionalProperties" attribute, which is a schema.
184 if (it
.key() == schema::kAdditionalProperties
) {
185 it
.value().GetAsDictionary(&dictionary_value
);
186 if (!IsValidSchema(dictionary_value
, error
)) {
187 DCHECK(!error
->empty());
192 // Validate the values contained in an "enum" attribute.
193 if (it
.key() == schema::kEnum
) {
194 it
.value().GetAsList(&list_value
);
195 for (size_t i
= 0; i
< list_value
->GetSize(); ++i
) {
196 const base::Value
* value
= NULL
;
197 list_value
->Get(i
, &value
);
198 switch (value
->GetType()) {
199 case base::Value::TYPE_NULL
:
200 case base::Value::TYPE_BOOLEAN
:
201 case base::Value::TYPE_INTEGER
:
202 case base::Value::TYPE_DOUBLE
:
203 case base::Value::TYPE_STRING
:
206 *error
= "Invalid value in enum attribute";
212 // Validate the schemas contained in a "choices" attribute.
213 if (it
.key() == schema::kChoices
) {
214 it
.value().GetAsList(&list_value
);
215 for (size_t i
= 0; i
< list_value
->GetSize(); ++i
) {
216 if (!list_value
->GetDictionary(i
, &dictionary_value
)) {
217 *error
= "Invalid choices attribute";
220 if (!IsValidSchema(dictionary_value
, error
)) {
221 DCHECK(!error
->empty());
229 *error
= "Schema must have a type attribute";
239 JSONSchemaValidator::Error::Error() {
242 JSONSchemaValidator::Error::Error(const std::string
& message
)
246 JSONSchemaValidator::Error::Error(const std::string
& path
,
247 const std::string
& message
)
248 : path(path
), message(message
) {
252 const char JSONSchemaValidator::kUnknownTypeReference
[] =
253 "Unknown schema reference: *.";
254 const char JSONSchemaValidator::kInvalidChoice
[] =
255 "Value does not match any valid type choices.";
256 const char JSONSchemaValidator::kInvalidEnum
[] =
257 "Value does not match any valid enum choices.";
258 const char JSONSchemaValidator::kObjectPropertyIsRequired
[] =
259 "Property is required.";
260 const char JSONSchemaValidator::kUnexpectedProperty
[] =
261 "Unexpected property.";
262 const char JSONSchemaValidator::kArrayMinItems
[] =
263 "Array must have at least * items.";
264 const char JSONSchemaValidator::kArrayMaxItems
[] =
265 "Array must not have more than * items.";
266 const char JSONSchemaValidator::kArrayItemRequired
[] =
268 const char JSONSchemaValidator::kStringMinLength
[] =
269 "String must be at least * characters long.";
270 const char JSONSchemaValidator::kStringMaxLength
[] =
271 "String must not be more than * characters long.";
272 const char JSONSchemaValidator::kStringPattern
[] =
273 "String must match the pattern: *.";
274 const char JSONSchemaValidator::kNumberMinimum
[] =
275 "Value must not be less than *.";
276 const char JSONSchemaValidator::kNumberMaximum
[] =
277 "Value must not be greater than *.";
278 const char JSONSchemaValidator::kInvalidType
[] =
279 "Expected '*' but got '*'.";
280 const char JSONSchemaValidator::kInvalidTypeIntegerNumber
[] =
281 "Expected 'integer' but got 'number', consider using Math.round().";
285 std::string
JSONSchemaValidator::GetJSONSchemaType(const base::Value
* value
) {
286 switch (value
->GetType()) {
287 case base::Value::TYPE_NULL
:
288 return schema::kNull
;
289 case base::Value::TYPE_BOOLEAN
:
290 return schema::kBoolean
;
291 case base::Value::TYPE_INTEGER
:
292 return schema::kInteger
;
293 case base::Value::TYPE_DOUBLE
: {
294 double double_value
= 0;
295 value
->GetAsDouble(&double_value
);
296 if (std::abs(double_value
) <= std::pow(2.0, DBL_MANT_DIG
) &&
297 double_value
== floor(double_value
)) {
298 return schema::kInteger
;
300 return schema::kNumber
;
303 case base::Value::TYPE_STRING
:
304 return schema::kString
;
305 case base::Value::TYPE_DICTIONARY
:
306 return schema::kObject
;
307 case base::Value::TYPE_LIST
:
308 return schema::kArray
;
310 NOTREACHED() << "Unexpected value type: " << value
->GetType();
311 return std::string();
316 std::string
JSONSchemaValidator::FormatErrorMessage(const std::string
& format
,
317 const std::string
& s1
) {
318 std::string ret_val
= format
;
319 ReplaceFirstSubstringAfterOffset(&ret_val
, 0, "*", s1
);
324 std::string
JSONSchemaValidator::FormatErrorMessage(const std::string
& format
,
325 const std::string
& s1
,
326 const std::string
& s2
) {
327 std::string ret_val
= format
;
328 ReplaceFirstSubstringAfterOffset(&ret_val
, 0, "*", s1
);
329 ReplaceFirstSubstringAfterOffset(&ret_val
, 0, "*", s2
);
334 scoped_ptr
<base::DictionaryValue
> JSONSchemaValidator::IsValidSchema(
335 const std::string
& schema
,
336 std::string
* error
) {
337 base::JSONParserOptions options
= base::JSON_PARSE_RFC
;
338 scoped_ptr
<base::Value
> json(
339 base::JSONReader::ReadAndReturnError(schema
, options
, NULL
, error
));
341 return scoped_ptr
<base::DictionaryValue
>();
342 base::DictionaryValue
* dict
= NULL
;
343 if (!json
->GetAsDictionary(&dict
)) {
344 *error
= "Schema must be a JSON object";
345 return scoped_ptr
<base::DictionaryValue
>();
347 if (!::IsValidSchema(dict
, error
))
348 return scoped_ptr
<base::DictionaryValue
>();
349 ignore_result(json
.release());
350 return make_scoped_ptr(dict
);
353 JSONSchemaValidator::JSONSchemaValidator(base::DictionaryValue
* schema
)
354 : schema_root_(schema
), default_allow_additional_properties_(false) {
357 JSONSchemaValidator::JSONSchemaValidator(base::DictionaryValue
* schema
,
358 base::ListValue
* types
)
359 : schema_root_(schema
), default_allow_additional_properties_(false) {
363 for (size_t i
= 0; i
< types
->GetSize(); ++i
) {
364 base::DictionaryValue
* type
= NULL
;
365 CHECK(types
->GetDictionary(i
, &type
));
368 CHECK(type
->GetString(schema::kId
, &id
));
370 CHECK(types_
.find(id
) == types_
.end());
375 JSONSchemaValidator::~JSONSchemaValidator() {}
377 bool JSONSchemaValidator::Validate(const base::Value
* instance
) {
379 Validate(instance
, schema_root_
, std::string());
380 return errors_
.empty();
383 void JSONSchemaValidator::Validate(const base::Value
* instance
,
384 const base::DictionaryValue
* schema
,
385 const std::string
& path
) {
386 // If this schema defines itself as reference type, save it in this.types.
388 if (schema
->GetString(schema::kId
, &id
)) {
389 TypeMap::iterator iter
= types_
.find(id
);
390 if (iter
== types_
.end())
393 DCHECK(iter
->second
== schema
);
396 // If the schema has a $ref property, the instance must validate against
397 // that schema. It must be present in types_ to be referenced.
399 if (schema
->GetString(schema::kRef
, &ref
)) {
400 TypeMap::iterator type
= types_
.find(ref
);
401 if (type
== types_
.end()) {
403 Error(path
, FormatErrorMessage(kUnknownTypeReference
, ref
)));
405 Validate(instance
, type
->second
, path
);
410 // If the schema has a choices property, the instance must validate against at
411 // least one of the items in that array.
412 const base::ListValue
* choices
= NULL
;
413 if (schema
->GetList(schema::kChoices
, &choices
)) {
414 ValidateChoices(instance
, choices
, path
);
418 // If the schema has an enum property, the instance must be one of those
420 const base::ListValue
* enumeration
= NULL
;
421 if (schema
->GetList(schema::kEnum
, &enumeration
)) {
422 ValidateEnum(instance
, enumeration
, path
);
427 schema
->GetString(schema::kType
, &type
);
428 CHECK(!type
.empty());
429 if (type
!= schema::kAny
) {
430 if (!ValidateType(instance
, type
, path
))
433 // These casts are safe because of checks in ValidateType().
434 if (type
== schema::kObject
) {
435 ValidateObject(static_cast<const base::DictionaryValue
*>(instance
),
438 } else if (type
== schema::kArray
) {
439 ValidateArray(static_cast<const base::ListValue
*>(instance
),
441 } else if (type
== schema::kString
) {
442 // Intentionally NOT downcasting to StringValue*. TYPE_STRING only implies
443 // GetAsString() can safely be carried out, not that it's a StringValue.
444 ValidateString(instance
, schema
, path
);
445 } else if (type
== schema::kNumber
|| type
== schema::kInteger
) {
446 ValidateNumber(instance
, schema
, path
);
447 } else if (type
!= schema::kBoolean
&& type
!= schema::kNull
) {
448 NOTREACHED() << "Unexpected type: " << type
;
453 void JSONSchemaValidator::ValidateChoices(const base::Value
* instance
,
454 const base::ListValue
* choices
,
455 const std::string
& path
) {
456 size_t original_num_errors
= errors_
.size();
458 for (size_t i
= 0; i
< choices
->GetSize(); ++i
) {
459 const base::DictionaryValue
* choice
= NULL
;
460 CHECK(choices
->GetDictionary(i
, &choice
));
462 Validate(instance
, choice
, path
);
463 if (errors_
.size() == original_num_errors
)
466 // We discard the error from each choice. We only want to know if any of the
467 // validations succeeded.
468 errors_
.resize(original_num_errors
);
471 // Now add a generic error that no choices matched.
472 errors_
.push_back(Error(path
, kInvalidChoice
));
476 void JSONSchemaValidator::ValidateEnum(const base::Value
* instance
,
477 const base::ListValue
* choices
,
478 const std::string
& path
) {
479 for (size_t i
= 0; i
< choices
->GetSize(); ++i
) {
480 const base::Value
* choice
= NULL
;
481 CHECK(choices
->Get(i
, &choice
));
482 switch (choice
->GetType()) {
483 case base::Value::TYPE_NULL
:
484 case base::Value::TYPE_BOOLEAN
:
485 case base::Value::TYPE_STRING
:
486 if (instance
->Equals(choice
))
490 case base::Value::TYPE_INTEGER
:
491 case base::Value::TYPE_DOUBLE
:
492 if (instance
->IsType(base::Value::TYPE_INTEGER
) ||
493 instance
->IsType(base::Value::TYPE_DOUBLE
)) {
494 if (GetNumberValue(choice
) == GetNumberValue(instance
))
500 NOTREACHED() << "Unexpected type in enum: " << choice
->GetType();
504 errors_
.push_back(Error(path
, kInvalidEnum
));
507 void JSONSchemaValidator::ValidateObject(const base::DictionaryValue
* instance
,
508 const base::DictionaryValue
* schema
,
509 const std::string
& path
) {
510 const base::DictionaryValue
* properties
= NULL
;
511 schema
->GetDictionary(schema::kProperties
, &properties
);
513 for (base::DictionaryValue::Iterator
it(*properties
); !it
.IsAtEnd();
515 std::string prop_path
= path
.empty() ? it
.key() : (path
+ "." + it
.key());
516 const base::DictionaryValue
* prop_schema
= NULL
;
517 CHECK(it
.value().GetAsDictionary(&prop_schema
));
519 const base::Value
* prop_value
= NULL
;
520 if (instance
->Get(it
.key(), &prop_value
)) {
521 Validate(prop_value
, prop_schema
, prop_path
);
523 // Properties are required unless there is an optional field set to
525 bool is_optional
= false;
526 prop_schema
->GetBoolean(schema::kOptional
, &is_optional
);
528 errors_
.push_back(Error(prop_path
, kObjectPropertyIsRequired
));
534 const base::DictionaryValue
* additional_properties_schema
= NULL
;
535 if (SchemaAllowsAnyAdditionalItems(schema
, &additional_properties_schema
))
538 // Validate additional properties.
539 for (base::DictionaryValue::Iterator
it(*instance
); !it
.IsAtEnd();
541 if (properties
&& properties
->HasKey(it
.key()))
544 std::string prop_path
= path
.empty() ? it
.key() : path
+ "." + it
.key();
545 if (!additional_properties_schema
) {
546 errors_
.push_back(Error(prop_path
, kUnexpectedProperty
));
548 Validate(&it
.value(), additional_properties_schema
, prop_path
);
553 void JSONSchemaValidator::ValidateArray(const base::ListValue
* instance
,
554 const base::DictionaryValue
* schema
,
555 const std::string
& path
) {
556 const base::DictionaryValue
* single_type
= NULL
;
557 size_t instance_size
= instance
->GetSize();
558 if (schema
->GetDictionary(schema::kItems
, &single_type
)) {
560 if (schema
->GetInteger(schema::kMinItems
, &min_items
)) {
561 CHECK(min_items
>= 0);
562 if (instance_size
< static_cast<size_t>(min_items
)) {
563 errors_
.push_back(Error(path
, FormatErrorMessage(
564 kArrayMinItems
, base::IntToString(min_items
))));
569 if (schema
->GetInteger(schema::kMaxItems
, &max_items
)) {
570 CHECK(max_items
>= 0);
571 if (instance_size
> static_cast<size_t>(max_items
)) {
572 errors_
.push_back(Error(path
, FormatErrorMessage(
573 kArrayMaxItems
, base::IntToString(max_items
))));
577 // If the items property is a single schema, each item in the array must
578 // validate against that schema.
579 for (size_t i
= 0; i
< instance_size
; ++i
) {
580 const base::Value
* item
= NULL
;
581 CHECK(instance
->Get(i
, &item
));
582 std::string i_str
= base::Uint64ToString(i
);
583 std::string item_path
= path
.empty() ? i_str
: (path
+ "." + i_str
);
584 Validate(item
, single_type
, item_path
);
590 // Otherwise, the list must be a tuple type, where each item in the list has a
591 // particular schema.
592 ValidateTuple(instance
, schema
, path
);
595 void JSONSchemaValidator::ValidateTuple(const base::ListValue
* instance
,
596 const base::DictionaryValue
* schema
,
597 const std::string
& path
) {
598 const base::ListValue
* tuple_type
= NULL
;
599 schema
->GetList(schema::kItems
, &tuple_type
);
600 size_t tuple_size
= tuple_type
? tuple_type
->GetSize() : 0;
602 for (size_t i
= 0; i
< tuple_size
; ++i
) {
603 std::string i_str
= base::Uint64ToString(i
);
604 std::string item_path
= path
.empty() ? i_str
: (path
+ "." + i_str
);
605 const base::DictionaryValue
* item_schema
= NULL
;
606 CHECK(tuple_type
->GetDictionary(i
, &item_schema
));
607 const base::Value
* item_value
= NULL
;
608 instance
->Get(i
, &item_value
);
609 if (item_value
&& item_value
->GetType() != base::Value::TYPE_NULL
) {
610 Validate(item_value
, item_schema
, item_path
);
612 bool is_optional
= false;
613 item_schema
->GetBoolean(schema::kOptional
, &is_optional
);
615 errors_
.push_back(Error(item_path
, kArrayItemRequired
));
622 const base::DictionaryValue
* additional_properties_schema
= NULL
;
623 if (SchemaAllowsAnyAdditionalItems(schema
, &additional_properties_schema
))
626 size_t instance_size
= instance
->GetSize();
627 if (additional_properties_schema
) {
628 // Any additional properties must validate against the additionalProperties
630 for (size_t i
= tuple_size
; i
< instance_size
; ++i
) {
631 std::string i_str
= base::Uint64ToString(i
);
632 std::string item_path
= path
.empty() ? i_str
: (path
+ "." + i_str
);
633 const base::Value
* item_value
= NULL
;
634 CHECK(instance
->Get(i
, &item_value
));
635 Validate(item_value
, additional_properties_schema
, item_path
);
637 } else if (instance_size
> tuple_size
) {
638 errors_
.push_back(Error(path
, FormatErrorMessage(
639 kArrayMaxItems
, base::Uint64ToString(tuple_size
))));
643 void JSONSchemaValidator::ValidateString(const base::Value
* instance
,
644 const base::DictionaryValue
* schema
,
645 const std::string
& path
) {
647 CHECK(instance
->GetAsString(&value
));
650 if (schema
->GetInteger(schema::kMinLength
, &min_length
)) {
651 CHECK(min_length
>= 0);
652 if (value
.size() < static_cast<size_t>(min_length
)) {
653 errors_
.push_back(Error(path
, FormatErrorMessage(
654 kStringMinLength
, base::IntToString(min_length
))));
659 if (schema
->GetInteger(schema::kMaxLength
, &max_length
)) {
660 CHECK(max_length
>= 0);
661 if (value
.size() > static_cast<size_t>(max_length
)) {
662 errors_
.push_back(Error(path
, FormatErrorMessage(
663 kStringMaxLength
, base::IntToString(max_length
))));
667 CHECK(!schema
->HasKey(schema::kPattern
)) << "Pattern is not supported.";
670 void JSONSchemaValidator::ValidateNumber(const base::Value
* instance
,
671 const base::DictionaryValue
* schema
,
672 const std::string
& path
) {
673 double value
= GetNumberValue(instance
);
675 // TODO(aa): It would be good to test that the double is not infinity or nan,
676 // but isnan and isinf aren't defined on Windows.
679 if (schema
->GetDouble(schema::kMinimum
, &minimum
)) {
681 errors_
.push_back(Error(path
, FormatErrorMessage(
682 kNumberMinimum
, base::DoubleToString(minimum
))));
686 if (schema
->GetDouble(schema::kMaximum
, &maximum
)) {
688 errors_
.push_back(Error(path
, FormatErrorMessage(
689 kNumberMaximum
, base::DoubleToString(maximum
))));
693 bool JSONSchemaValidator::ValidateType(const base::Value
* instance
,
694 const std::string
& expected_type
,
695 const std::string
& path
) {
696 std::string actual_type
= GetJSONSchemaType(instance
);
697 if (expected_type
== actual_type
||
698 (expected_type
== schema::kNumber
&& actual_type
== schema::kInteger
)) {
700 } else if (expected_type
== schema::kInteger
&&
701 actual_type
== schema::kNumber
) {
702 errors_
.push_back(Error(path
, kInvalidTypeIntegerNumber
));
705 errors_
.push_back(Error(path
, FormatErrorMessage(
706 kInvalidType
, expected_type
, actual_type
)));
711 bool JSONSchemaValidator::SchemaAllowsAnyAdditionalItems(
712 const base::DictionaryValue
* schema
,
713 const base::DictionaryValue
** additional_properties_schema
) {
714 // If the validator allows additional properties globally, and this schema
715 // doesn't override, then we can exit early.
716 schema
->GetDictionary(schema::kAdditionalProperties
,
717 additional_properties_schema
);
719 if (*additional_properties_schema
) {
720 std::string
additional_properties_type(schema::kAny
);
721 CHECK((*additional_properties_schema
)->GetString(
722 schema::kType
, &additional_properties_type
));
723 return additional_properties_type
== schema::kAny
;
725 return default_allow_additional_properties_
;