diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ed193b39..6de511e9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). * Fix memory leak for invalid regex (26e69ffde95ba4773ab06db6457b78f308716f4b) * Fix a bug in schema minimum/maximum keywords for 64-bit integer (e7149d665941068ccf8c565e77495521331cf390) * Fix a crash bug in regex (#605) +* Fix schema "required" keyword cannot handle duplicated keys (#609) ### Changed * Clarify problematic JSON license (#392) diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index 33b6a10f2..0a8bb7c5f 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -289,7 +289,7 @@ struct SchemaValidationContext { patternPropertiesSchemas(), patternPropertiesSchemaCount(), valuePatternValidatorType(kPatternValidatorOnly), - objectDependencies(), + propertyExist(), inArray(false), valueUniqueness(false), arrayUniqueness(false) @@ -311,8 +311,8 @@ struct SchemaValidationContext { } if (patternPropertiesSchemas) factory.FreeState(patternPropertiesSchemas); - if (objectDependencies) - factory.FreeState(objectDependencies); + if (propertyExist) + factory.FreeState(propertyExist); } SchemaValidatorFactoryType& factory; @@ -329,9 +329,8 @@ struct SchemaValidationContext { SizeType patternPropertiesSchemaCount; PatternValidatorType valuePatternValidatorType; PatternValidatorType objectPatternValidatorType; - SizeType objectRequiredCount; SizeType arrayElementIndex; - bool* objectDependencies; + bool* propertyExist; bool inArray; bool valueUniqueness; bool arrayUniqueness; @@ -365,11 +364,11 @@ class Schema { patternProperties_(), patternPropertyCount_(), propertyCount_(), - requiredCount_(), minProperties_(), maxProperties_(SizeType(~0)), additionalProperties_(true), hasDependencies_(), + hasRequired_(), hasSchemaDependencies_(), additionalItemsSchema_(), itemsList_(), @@ -490,7 +489,7 @@ class Schema { SizeType index; if (FindPropertyIndex(*itr, &index)) { properties_[index].required = true; - requiredCount_++; + hasRequired_ = true; } } @@ -767,10 +766,9 @@ class Schema { if (!(type_ & (1 << kObjectSchemaType))) RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); - context.objectRequiredCount = 0; - if (hasDependencies_) { - context.objectDependencies = static_cast(context.factory.MallocState(sizeof(bool) * propertyCount_)); - std::memset(context.objectDependencies, 0, sizeof(bool) * propertyCount_); + if (hasDependencies_ || hasRequired_) { + context.propertyExist = static_cast(context.factory.MallocState(sizeof(bool) * propertyCount_)); + std::memset(context.propertyExist, 0, sizeof(bool) * propertyCount_); } if (patternProperties_) { // pre-allocate schema array @@ -801,11 +799,8 @@ class Schema { else context.valueSchema = properties_[index].schema; - if (properties_[index].required) - context.objectRequiredCount++; - - if (hasDependencies_) - context.objectDependencies[index] = true; + if (context.propertyExist) + context.propertyExist[index] = true; return true; } @@ -832,8 +827,11 @@ class Schema { } bool EndObject(Context& context, SizeType memberCount) const { - if (context.objectRequiredCount != requiredCount_) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString()); + if (hasRequired_) + for (SizeType index = 0; index < propertyCount_; index++) + if (properties_[index].required) + if (!context.propertyExist[index]) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString()); if (memberCount < minProperties_) RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString()); @@ -843,10 +841,10 @@ class Schema { if (hasDependencies_) { for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) - if (context.objectDependencies[sourceIndex]) { + if (context.propertyExist[sourceIndex]) { if (properties_[sourceIndex].dependencies) { for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++) - if (properties_[sourceIndex].dependencies[targetIndex] && !context.objectDependencies[targetIndex]) + if (properties_[sourceIndex].dependencies[targetIndex] && !context.propertyExist[targetIndex]) RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString()); } else if (properties_[sourceIndex].dependenciesSchema) @@ -1236,11 +1234,11 @@ class Schema { PatternProperty* patternProperties_; SizeType patternPropertyCount_; SizeType propertyCount_; - SizeType requiredCount_; SizeType minProperties_; SizeType maxProperties_; bool additionalProperties_; bool hasDependencies_; + bool hasRequired_; bool hasSchemaDependencies_; const SchemaType* additionalItemsSchema_; diff --git a/test/unittest/schematest.cpp b/test/unittest/schematest.cpp index ff8b5d6ea..d1027ad51 100644 --- a/test/unittest/schematest.cpp +++ b/test/unittest/schematest.cpp @@ -1299,6 +1299,15 @@ TEST(Schema, Issue552) { #endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS +TEST(SchemaValidator, Issue608) { + Document sd; + sd.Parse("{\"required\": [\"a\", \"b\"] }"); + SchemaDocument s(sd); + + VALIDATE(s, "{\"a\" : null, \"b\": null}", true); + INVALIDATE(s, "{\"a\" : null, \"a\" : null}", "", "required", ""); +} + #ifdef __clang__ RAPIDJSON_DIAG_POP #endif