From 05ae593583a04960f45ac16374b2d7334e05cb0d Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sat, 2 May 2015 10:06:48 +0800 Subject: [PATCH] Implement additionalItems --- include/rapidjson/schema.h | 59 +++++++++++++++++++++++++----------- test/unittest/schematest.cpp | 30 ++++++++++++++++++ 2 files changed, 71 insertions(+), 18 deletions(-) diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index d480778..0c5a2b7 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -64,7 +64,7 @@ public: virtual ~BaseSchema() {} - virtual void BeginValue(Context&) const {} + virtual bool BeginValue(Context&) const { return true; } virtual bool Null() const { return enum_.IsArray() ? CheckEnum(GenericValue().Move()) : true; } virtual bool Bool(bool b) const { return enum_.IsArray() ? CheckEnum(GenericValue(b).Move()) : true; } @@ -105,7 +105,7 @@ public: template TypelessSchema(const ValueType& value) : BaseSchema(value) {} - virtual void BeginValue(Context& context) const { context.valueSchema = this; } + virtual bool BeginValue(Context& context) const { context.valueSchema = this; return true; } }; template @@ -347,7 +347,8 @@ public: itemsTuple_(), itemsTupleCount_(), minItems_(), - maxItems_(SizeType(~0)) + maxItems_(SizeType(~0)), + additionalItems_(true) { typename ValueType::ConstMemberIterator itemsItr = value.FindMember(Value("items").Move()); if (itemsItr != value.MemberEnd()) { @@ -383,6 +384,15 @@ public: // Error } } + + typename ValueType::ConstMemberIterator additionalItemsItr = value.FindMember(Value("additionalItems").Move()); + if (additionalItemsItr != value.MemberEnd()) { + if (additionalItemsItr->value.IsBool()) + additionalItems_ = maxItemsItr->value.GetBool(); + else { + // Error + } + } } ~ArraySchema() { @@ -392,15 +402,22 @@ public: delete [] itemsTuple_; } - virtual void BeginValue(Context& context) const { + virtual bool BeginValue(Context& context) const { if (itemsList_) context.valueSchema = itemsList_; - else if (itemsTuple_ && context.arrayElementIndex < itemsTupleCount_) - context.valueSchema = itemsTuple_[context.arrayElementIndex]; + else if (itemsTuple_) { + if (context.arrayElementIndex < itemsTupleCount_) + context.valueSchema = itemsTuple_[context.arrayElementIndex]; + else if (additionalItems_) + context.valueSchema = &typeless_; + else + return false; + } else context.valueSchema = &typeless_; context.arrayElementIndex++; + return true; } virtual bool Null() const { return false; } @@ -431,6 +448,7 @@ private: SizeType itemsTupleCount_; SizeType minItems_; SizeType maxItems_; + bool additionalItems_; }; template @@ -804,15 +822,17 @@ public: documentStack_.Clear(); }; - bool Null() { BeginValue(); return PopSchema().Null() ? outputHandler_.Null() : false; } - bool Bool(bool b) { BeginValue(); return PopSchema().Bool(b) ? outputHandler_.Bool(b) : false; } - bool Int(int i) { BeginValue(); return PopSchema().Int(i) ? outputHandler_.Int(i) : false; } - bool Uint(unsigned u) { BeginValue(); return PopSchema().Uint(u) ? outputHandler_.Uint(u) : false; } - bool Int64(int64_t i64) { BeginValue(); return PopSchema().Int64(i64) ? outputHandler_.Int64(i64) : false; } - bool Uint64(uint64_t u64) { BeginValue(); return PopSchema().Uint64(u64) ? outputHandler_.Uint64(u64) : false; } - bool Double(double d) { BeginValue(); return PopSchema().Double(d) ? outputHandler_.Double(d) : false; } - bool String(const Ch* str, SizeType length, bool copy) { BeginValue(); return PopSchema().String(str, length, copy) ? outputHandler_.String(str, length, copy) : false; } - bool StartObject() { BeginValue(); return CurrentSchema().StartObject(CurrentContext()) ? outputHandler_.StartObject() : false; } + bool Null() { return BeginValue() && PopSchema().Null() ? outputHandler_.Null() : false; } + bool Bool(bool b) { return BeginValue() && PopSchema().Bool(b) ? outputHandler_.Bool(b) : false; } + bool Int(int i) { return BeginValue() && PopSchema().Int(i) ? outputHandler_.Int(i) : false; } + bool Uint(unsigned u) { return BeginValue() && PopSchema().Uint(u) ? outputHandler_.Uint(u) : false; } + bool Int64(int64_t i64) { return BeginValue() && PopSchema().Int64(i64) ? outputHandler_.Int64(i64) : false; } + bool Uint64(uint64_t u64) { return BeginValue() && PopSchema().Uint64(u64) ? outputHandler_.Uint64(u64) : false; } + bool Double(double d) { return BeginValue() && PopSchema().Double(d) ? outputHandler_.Double(d) : false; } + bool String(const Ch* str, SizeType length, bool copy) { return BeginValue() && PopSchema().String(str, length, copy) ? outputHandler_.String(str, length, copy) : false; } + + bool StartObject() { return BeginValue() && CurrentSchema().StartObject(CurrentContext()) ? outputHandler_.StartObject() : false; } + bool Key(const Ch* str, SizeType len, bool copy) { return CurrentSchema().Key(CurrentContext(), str, len, copy) ? outputHandler_.Key(str, len, copy) : false; } bool EndObject(SizeType memberCount) { @@ -824,7 +844,7 @@ public: return false; } - bool StartArray() { BeginValue(); return CurrentSchema().StartArray(CurrentContext()) ? outputHandler_.StartArray(): false; } + bool StartArray() { return BeginValue() && CurrentSchema().StartArray(CurrentContext()) ? outputHandler_.StartArray() : false; } bool EndArray(SizeType elementCount) { if (CurrentSchema().EndArray(CurrentContext(), elementCount)) { @@ -839,12 +859,15 @@ private: typedef BaseSchema BaseSchemaType; typedef typename BaseSchemaType::Context Context; - void BeginValue() { + bool BeginValue() { if (schemaStack_.Empty()) { PushSchema(*schema_.root_); + return true; } else { - CurrentSchema().BeginValue(CurrentContext()); + if (!CurrentSchema().BeginValue(CurrentContext())) + return false; + if (CurrentContext().valueSchema) PushSchema(*CurrentContext().valueSchema); } diff --git a/test/unittest/schematest.cpp b/test/unittest/schematest.cpp index ee8ce8a..d675150 100644 --- a/test/unittest/schematest.cpp +++ b/test/unittest/schematest.cpp @@ -350,6 +350,36 @@ TEST(SchemaValidator, Array_ItemsTuple) { VALIDATE(s, "[1600, \"Pennsylvania\", \"Avenue\", \"NW\", \"Washington\"]", true); } +TEST(SchemaValidator, Array_AdditionalItmes) { + Document sd; + sd.Parse( + "{" + " \"type\": \"array\"," + " \"items\": [" + " {" + " \"type\": \"number\"" + " }," + " {" + " \"type\": \"string\"" + " }," + " {" + " \"type\": \"string\"," + " \"enum\": [\"Street\", \"Avenue\", \"Boulevard\"]" + " }," + " {" + " \"type\": \"string\"," + " \"enum\": [\"NW\", \"NE\", \"SW\", \"SE\"]" + " }" + " ]," + " \"additionalItems\": false" + "}"); + Schema s(sd); + + VALIDATE(s, "[1600, \"Pennsylvania\", \"Avenue\", \"NW\"]", true); + VALIDATE(s, "[1600, \"Pennsylvania\", \"Avenue\"]", true); + VALIDATE(s, "[1600, \"Pennsylvania\", \"Avenue\", \"NW\", \"Washington\"]", false); +} + TEST(SchemaValidator, Array_ItemsRange) { Document sd; sd.Parse("{\"type\": \"array\",\"minItems\": 2,\"maxItems\" : 3}");