Add invalid schema/document pointers
This commit is contained in:
parent
6b7e7d769d
commit
5bc9523cbf
@ -1035,6 +1035,8 @@ public:
|
|||||||
typedef internal::Schema<GenericSchemaDocument> SchemaType;
|
typedef internal::Schema<GenericSchemaDocument> SchemaType;
|
||||||
typedef GenericPointer<ValueType, CrtAllocator> PointerType;
|
typedef GenericPointer<ValueType, CrtAllocator> PointerType;
|
||||||
friend class internal::Schema<GenericSchemaDocument>;
|
friend class internal::Schema<GenericSchemaDocument>;
|
||||||
|
template <typename, typename, typename>
|
||||||
|
friend class GenericSchemaValidator;
|
||||||
|
|
||||||
GenericSchemaDocument(const ValueType& document, IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) :
|
GenericSchemaDocument(const ValueType& document, IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) :
|
||||||
remoteProvider_(remoteProvider),
|
remoteProvider_(remoteProvider),
|
||||||
@ -1136,6 +1138,13 @@ private:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PointerType GetPointer(const SchemaType* schema) const {
|
||||||
|
for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target)
|
||||||
|
if (schema == target->schema)
|
||||||
|
return target->pointer;
|
||||||
|
return PointerType();
|
||||||
|
}
|
||||||
|
|
||||||
static const size_t kInitialSchemaMapSize = 64;
|
static const size_t kInitialSchemaMapSize = 64;
|
||||||
static const size_t kInitialSchemaRefSize = 64;
|
static const size_t kInitialSchemaRefSize = 64;
|
||||||
|
|
||||||
@ -1160,19 +1169,21 @@ class GenericSchemaValidator :
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef typename SchemaDocumentType::SchemaType SchemaType;
|
typedef typename SchemaDocumentType::SchemaType SchemaType;
|
||||||
|
typedef typename SchemaDocumentType::PointerType PointerType;
|
||||||
typedef typename SchemaType::EncodingType EncodingType;
|
typedef typename SchemaType::EncodingType EncodingType;
|
||||||
typedef typename EncodingType::Ch Ch;
|
typedef typename EncodingType::Ch Ch;
|
||||||
|
|
||||||
GenericSchemaValidator(
|
GenericSchemaValidator(
|
||||||
const SchemaDocumentType& schemaDocument,
|
const SchemaDocumentType& schemaDocument,
|
||||||
StateAllocator* allocator = 0,
|
StateAllocator* allocator = 0,
|
||||||
size_t schemaStackCapacity = kDefaultSchemaStackCapacity/*,
|
size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
|
||||||
size_t documentStackCapacity = kDefaultDocumentStackCapacity*/)
|
size_t documentStackCapacity = kDefaultDocumentStackCapacity)
|
||||||
:
|
:
|
||||||
|
schemaDocument_(&schemaDocument),
|
||||||
root_(schemaDocument.GetRoot()),
|
root_(schemaDocument.GetRoot()),
|
||||||
outputHandler_(nullOutputHandler_),
|
outputHandler_(nullOutputHandler_),
|
||||||
schemaStack_(allocator, schemaStackCapacity),
|
schemaStack_(allocator, schemaStackCapacity),
|
||||||
// documentStack_(allocator, documentStackCapacity),
|
documentStack_(allocator, documentStackCapacity),
|
||||||
valid_(true)
|
valid_(true)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -1181,13 +1192,14 @@ public:
|
|||||||
const SchemaDocumentType& schemaDocument,
|
const SchemaDocumentType& schemaDocument,
|
||||||
OutputHandler& outputHandler,
|
OutputHandler& outputHandler,
|
||||||
StateAllocator* allocator = 0,
|
StateAllocator* allocator = 0,
|
||||||
size_t schemaStackCapacity = kDefaultSchemaStackCapacity/*,
|
size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
|
||||||
size_t documentStackCapacity = kDefaultDocumentStackCapacity*/)
|
size_t documentStackCapacity = kDefaultDocumentStackCapacity)
|
||||||
:
|
:
|
||||||
|
schemaDocument_(&schemaDocument),
|
||||||
root_(schemaDocument.GetRoot()),
|
root_(schemaDocument.GetRoot()),
|
||||||
outputHandler_(outputHandler),
|
outputHandler_(outputHandler),
|
||||||
schemaStack_(allocator, schemaStackCapacity),
|
schemaStack_(allocator, schemaStackCapacity),
|
||||||
// documentStack_(allocator, documentStackCapacity),
|
documentStack_(allocator, documentStackCapacity),
|
||||||
valid_(true)
|
valid_(true)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -1206,6 +1218,14 @@ public:
|
|||||||
// Implementation of ISchemaValidator
|
// Implementation of ISchemaValidator
|
||||||
virtual bool IsValid() const { return valid_; }
|
virtual bool IsValid() const { return valid_; }
|
||||||
|
|
||||||
|
PointerType GetInvalidSchemaPointer() const {
|
||||||
|
return schemaStack_.Empty() ? PointerType() : schemaDocument_->GetPointer(&CurrentSchema());
|
||||||
|
}
|
||||||
|
|
||||||
|
PointerType GetInvalidDocumentPointer() const {
|
||||||
|
return documentStack_.Empty() ? PointerType() : PointerType(documentStack_.template Bottom<Ch>(), documentStack_.GetSize() / sizeof(Ch));
|
||||||
|
}
|
||||||
|
|
||||||
#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\
|
#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\
|
||||||
if (!valid_) return false; \
|
if (!valid_) return false; \
|
||||||
if (!BeginValue() || !CurrentSchema().method arg1) return valid_ = false;
|
if (!BeginValue() || !CurrentSchema().method arg1) return valid_ = false;
|
||||||
@ -1263,6 +1283,7 @@ public:
|
|||||||
|
|
||||||
bool Key(const Ch* str, SizeType len, bool copy) {
|
bool Key(const Ch* str, SizeType len, bool copy) {
|
||||||
if (!valid_) return false;
|
if (!valid_) return false;
|
||||||
|
AppendToken(str, len);
|
||||||
if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false;
|
if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false;
|
||||||
RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy));
|
RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy));
|
||||||
return valid_ = outputHandler_.Key(str, len, copy);
|
return valid_ = outputHandler_.Key(str, len, copy);
|
||||||
@ -1303,13 +1324,14 @@ private:
|
|||||||
GenericSchemaValidator(
|
GenericSchemaValidator(
|
||||||
const SchemaType& root,
|
const SchemaType& root,
|
||||||
StateAllocator* allocator = 0,
|
StateAllocator* allocator = 0,
|
||||||
size_t schemaStackCapacity = kDefaultSchemaStackCapacity/*,
|
size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
|
||||||
size_t documentStackCapacity = kDefaultDocumentStackCapacity*/)
|
size_t documentStackCapacity = kDefaultDocumentStackCapacity)
|
||||||
:
|
:
|
||||||
|
schemaDocument_(),
|
||||||
root_(root),
|
root_(root),
|
||||||
outputHandler_(nullOutputHandler_),
|
outputHandler_(nullOutputHandler_),
|
||||||
schemaStack_(allocator, schemaStackCapacity),
|
schemaStack_(allocator, schemaStackCapacity),
|
||||||
// documentStack_(allocator, documentStackCapacity),
|
documentStack_(allocator, documentStackCapacity),
|
||||||
valid_(true)
|
valid_(true)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -1318,6 +1340,9 @@ private:
|
|||||||
if (schemaStack_.Empty())
|
if (schemaStack_.Empty())
|
||||||
PushSchema(root_);
|
PushSchema(root_);
|
||||||
else {
|
else {
|
||||||
|
if (CurrentContext().inArray)
|
||||||
|
AppendToken(CurrentContext().arrayElementIndex);
|
||||||
|
|
||||||
if (!CurrentSchema().BeginValue(CurrentContext()))
|
if (!CurrentSchema().BeginValue(CurrentContext()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -1345,6 +1370,12 @@ private:
|
|||||||
if (!CurrentSchema().EndValue(CurrentContext()))
|
if (!CurrentSchema().EndValue(CurrentContext()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// *documentStack_.template Push<Ch>() = '\0';
|
||||||
|
// documentStack_.template Pop<Ch>(1);
|
||||||
|
// printf("document: %s\n", documentStack_.template Bottom<Ch>());
|
||||||
|
while (!documentStack_.Empty() && *documentStack_.template Pop<Ch>(1) != '/')
|
||||||
|
;
|
||||||
|
|
||||||
uint64_t h = CurrentContext().arrayUniqueness ? CurrentContext().hasher->GetHashCode() : 0;
|
uint64_t h = CurrentContext().arrayUniqueness ? CurrentContext().hasher->GetHashCode() : 0;
|
||||||
|
|
||||||
PopSchema();
|
PopSchema();
|
||||||
@ -1362,19 +1393,44 @@ private:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AppendToken(const Ch* str, SizeType len) {
|
||||||
|
*documentStack_.template Push<Ch>() = '/';
|
||||||
|
for (SizeType i = 0; i < len; i++) {
|
||||||
|
if (str[i] == '~') {
|
||||||
|
*documentStack_.template Push<Ch>() = '~';
|
||||||
|
*documentStack_.template Push<Ch>() = '0';
|
||||||
|
}
|
||||||
|
else if (str[i] == '/') {
|
||||||
|
*documentStack_.template Push<Ch>() = '~';
|
||||||
|
*documentStack_.template Push<Ch>() = '1';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
*documentStack_.template Push<Ch>() = str[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppendToken(SizeType index) {
|
||||||
|
*documentStack_.template Push<Ch>() = '/';
|
||||||
|
char buffer[21];
|
||||||
|
SizeType length = (sizeof(SizeType) == 4 ? internal::u32toa(index, buffer): internal::u64toa(index, buffer)) - buffer;
|
||||||
|
for (SizeType i = 0; i < length; i++)
|
||||||
|
*documentStack_.template Push<Ch>() = buffer[i];
|
||||||
|
}
|
||||||
|
|
||||||
void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push<Context>()) Context(this, &contextAllocator_, &schema); }
|
void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push<Context>()) Context(this, &contextAllocator_, &schema); }
|
||||||
void PopSchema() { schemaStack_.template Pop<Context>(1)->~Context(); }
|
void PopSchema() { schemaStack_.template Pop<Context>(1)->~Context(); }
|
||||||
const SchemaType& CurrentSchema() { return *schemaStack_.template Top<Context>()->schema; }
|
const SchemaType& CurrentSchema() const { return *schemaStack_.template Top<Context>()->schema; }
|
||||||
Context& CurrentContext() { return *schemaStack_.template Top<Context>(); }
|
Context& CurrentContext() { return *schemaStack_.template Top<Context>(); }
|
||||||
|
|
||||||
static const size_t kDefaultSchemaStackCapacity = 1024;
|
static const size_t kDefaultSchemaStackCapacity = 1024;
|
||||||
//static const size_t kDefaultDocumentStackCapacity = 256;
|
static const size_t kDefaultDocumentStackCapacity = 256;
|
||||||
|
const SchemaDocument* schemaDocument_;
|
||||||
const SchemaType& root_;
|
const SchemaType& root_;
|
||||||
BaseReaderHandler<EncodingType> nullOutputHandler_;
|
BaseReaderHandler<EncodingType> nullOutputHandler_;
|
||||||
OutputHandler& outputHandler_;
|
OutputHandler& outputHandler_;
|
||||||
CrtAllocator contextAllocator_;
|
CrtAllocator contextAllocator_;
|
||||||
internal::Stack<StateAllocator> schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *)
|
internal::Stack<StateAllocator> schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *)
|
||||||
//internal::Stack<Allocator> documentStack_; //!< stack to store the current path of validating document (Value *)
|
internal::Stack<StateAllocator> documentStack_; //!< stack to store the current path of validating document (Ch)
|
||||||
bool valid_;
|
bool valid_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
#include "unittest.h"
|
#include "unittest.h"
|
||||||
#include "rapidjson/schema.h"
|
#include "rapidjson/schema.h"
|
||||||
|
#include "rapidjson/stringbuffer.h"
|
||||||
|
|
||||||
using namespace rapidjson;
|
using namespace rapidjson;
|
||||||
|
|
||||||
@ -94,11 +95,41 @@ TEST(SchemaValidator, Hasher) {
|
|||||||
{\
|
{\
|
||||||
SchemaValidator validator(schema);\
|
SchemaValidator validator(schema);\
|
||||||
Document d;\
|
Document d;\
|
||||||
/*printf("\n%s\n", json);*/\
|
|
||||||
d.Parse(json);\
|
d.Parse(json);\
|
||||||
EXPECT_FALSE(d.HasParseError());\
|
EXPECT_FALSE(d.HasParseError());\
|
||||||
EXPECT_TRUE(expected == d.Accept(validator));\
|
EXPECT_TRUE(expected == d.Accept(validator));\
|
||||||
EXPECT_TRUE(expected == validator.IsValid());\
|
EXPECT_TRUE(expected == validator.IsValid());\
|
||||||
|
/*if (!validator.IsValid()) {\
|
||||||
|
StringBuffer sb;\
|
||||||
|
validator.GetInvalidSchemaPointer().StringifyUriFragment(sb);\
|
||||||
|
printf("Error schema: %s\n", sb.GetString());\
|
||||||
|
sb.Clear();\
|
||||||
|
validator.GetInvalidDocumentPointer().StringifyUriFragment(sb);\
|
||||||
|
printf("Error document: %s\n", sb.GetString());\
|
||||||
|
}*/\
|
||||||
|
}
|
||||||
|
|
||||||
|
#define VALIDATE_ERROR(schema, json, invalidSchemaPointer, invalidDocumentPointer) \
|
||||||
|
{\
|
||||||
|
SchemaValidator validator(schema);\
|
||||||
|
Document d;\
|
||||||
|
/*printf("\n%s\n", json);*/\
|
||||||
|
d.Parse(json);\
|
||||||
|
EXPECT_FALSE(d.HasParseError());\
|
||||||
|
EXPECT_FALSE(d.Accept(validator));\
|
||||||
|
EXPECT_FALSE(validator.IsValid());\
|
||||||
|
if (validator.GetInvalidSchemaPointer() != Pointer(invalidSchemaPointer)) {\
|
||||||
|
StringBuffer sb;\
|
||||||
|
validator.GetInvalidSchemaPointer().Stringify(sb);\
|
||||||
|
printf("GetInvalidSchemaPointer() Expected: %s Actual: %s\n", invalidSchemaPointer, sb.GetString());\
|
||||||
|
ADD_FAILURE();\
|
||||||
|
}\
|
||||||
|
if (validator.GetInvalidDocumentPointer() != Pointer(invalidDocumentPointer)) {\
|
||||||
|
StringBuffer sb;\
|
||||||
|
validator.GetInvalidDocumentPointer().Stringify(sb);\
|
||||||
|
printf("GetInvalidDocumentPointer() Expected: %s Actual: %s\n", invalidDocumentPointer, sb.GetString());\
|
||||||
|
ADD_FAILURE();\
|
||||||
|
}\
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(SchemaValidator, Typeless) {
|
TEST(SchemaValidator, Typeless) {
|
||||||
@ -118,7 +149,7 @@ TEST(SchemaValidator, MultiType) {
|
|||||||
|
|
||||||
VALIDATE(s, "42", true);
|
VALIDATE(s, "42", true);
|
||||||
VALIDATE(s, "\"Life, the universe, and everything\"", true);
|
VALIDATE(s, "\"Life, the universe, and everything\"", true);
|
||||||
VALIDATE(s, "[\"Life\", \"the universe\", \"and everything\"]", false);
|
VALIDATE_ERROR(s, "[\"Life\", \"the universe\", \"and everything\"]", "", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(SchemaValidator, Enum_Typed) {
|
TEST(SchemaValidator, Enum_Typed) {
|
||||||
@ -153,10 +184,10 @@ TEST(SchemaValidator, Enum_InvalidType) {
|
|||||||
TEST(SchemaValidator, AllOf) {
|
TEST(SchemaValidator, AllOf) {
|
||||||
{
|
{
|
||||||
Document sd;
|
Document sd;
|
||||||
sd.Parse("{\"allOf\": [{ \"type\": \"string\" }, { \"type\": \"string\", \"maxLength\": 5 }]}"); // need "type": "string" now
|
sd.Parse("{\"allOf\": [{ \"type\": \"string\" }, { \"type\": \"string\", \"maxLength\": 5 }]}");
|
||||||
SchemaDocument s(sd);
|
SchemaDocument s(sd);
|
||||||
|
|
||||||
//VALIDATE(s, "\"ok\"", true);
|
VALIDATE(s, "\"ok\"", true);
|
||||||
VALIDATE(s, "\"too long\"", false);
|
VALIDATE(s, "\"too long\"", false);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
@ -261,7 +292,7 @@ TEST(SchemaValidator, Ref_AllOf) {
|
|||||||
"}");
|
"}");
|
||||||
SchemaDocument s(sd);
|
SchemaDocument s(sd);
|
||||||
|
|
||||||
VALIDATE(s, "{\"shipping_address\": {\"street_address\": \"1600 Pennsylvania Avenue NW\", \"city\": \"Washington\", \"state\": \"DC\"} }", false);
|
VALIDATE_ERROR(s, "{\"shipping_address\": {\"street_address\": \"1600 Pennsylvania Avenue NW\", \"city\": \"Washington\", \"state\": \"DC\"} }", "/properties/shipping_address", "/shipping_address");
|
||||||
VALIDATE(s, "{\"shipping_address\": {\"street_address\": \"1600 Pennsylvania Avenue NW\", \"city\": \"Washington\", \"state\": \"DC\", \"type\": \"business\"} }", true);
|
VALIDATE(s, "{\"shipping_address\": {\"street_address\": \"1600 Pennsylvania Avenue NW\", \"city\": \"Washington\", \"state\": \"DC\", \"type\": \"business\"} }", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user