Add Ref in schema
This commit is contained in:
parent
1e4a3818ed
commit
f0c3fa84fc
@ -116,6 +116,7 @@ struct SchemaValidationContext {
|
|||||||
valueSchema(),
|
valueSchema(),
|
||||||
patternPropertiesSchemas(),
|
patternPropertiesSchemas(),
|
||||||
notValidator(),
|
notValidator(),
|
||||||
|
refValidator(),
|
||||||
patternPropertiesSchemaCount(),
|
patternPropertiesSchemaCount(),
|
||||||
valuePatternValidatorType(kPatternValidatorOnly),
|
valuePatternValidatorType(kPatternValidatorOnly),
|
||||||
objectDependencies(),
|
objectDependencies(),
|
||||||
@ -125,6 +126,7 @@ struct SchemaValidationContext {
|
|||||||
|
|
||||||
~SchemaValidationContext() {
|
~SchemaValidationContext() {
|
||||||
delete notValidator;
|
delete notValidator;
|
||||||
|
delete refValidator;
|
||||||
delete[] patternPropertiesSchemas;
|
delete[] patternPropertiesSchemas;
|
||||||
delete[] objectDependencies;
|
delete[] objectDependencies;
|
||||||
}
|
}
|
||||||
@ -139,6 +141,7 @@ struct SchemaValidationContext {
|
|||||||
SchemaValidatorArray patternPropertiesValidators;
|
SchemaValidatorArray patternPropertiesValidators;
|
||||||
const SchemaType** patternPropertiesSchemas;
|
const SchemaType** patternPropertiesSchemas;
|
||||||
ISchemaValidator* notValidator;
|
ISchemaValidator* notValidator;
|
||||||
|
ISchemaValidator* refValidator;
|
||||||
SizeType patternPropertiesSchemaCount;
|
SizeType patternPropertiesSchemaCount;
|
||||||
PatternValidatorType valuePatternValidatorType;
|
PatternValidatorType valuePatternValidatorType;
|
||||||
PatternValidatorType objectPatternValidatorType;
|
PatternValidatorType objectPatternValidatorType;
|
||||||
@ -158,10 +161,12 @@ public:
|
|||||||
typedef Schema<Encoding, Allocator> SchemaType;
|
typedef Schema<Encoding, Allocator> SchemaType;
|
||||||
typedef GenericValue<Encoding, Allocator> ValueType;
|
typedef GenericValue<Encoding, Allocator> ValueType;
|
||||||
typedef GenericPointer<ValueType> PointerType;
|
typedef GenericPointer<ValueType> PointerType;
|
||||||
|
friend class GenericSchemaDocument<Encoding, Allocator>;
|
||||||
|
|
||||||
template <typename ValueType>
|
template <typename ValueType>
|
||||||
Schema(SchemaDocumentType* document, const PointerType& p, const ValueType& value) :
|
Schema(SchemaDocumentType* document, const PointerType& p, const ValueType& value) :
|
||||||
not_(),
|
not_(),
|
||||||
|
ref_(),
|
||||||
type_((1 << kTotalSchemaType) - 1), // typeless
|
type_((1 << kTotalSchemaType) - 1), // typeless
|
||||||
properties_(),
|
properties_(),
|
||||||
additionalPropertiesSchema_(),
|
additionalPropertiesSchema_(),
|
||||||
@ -217,6 +222,9 @@ public:
|
|||||||
if (const ValueType* v = GetMember(value, "not"))
|
if (const ValueType* v = GetMember(value, "not"))
|
||||||
not_ = document->CreateSchema(p.Append("not"), *v);
|
not_ = document->CreateSchema(p.Append("not"), *v);
|
||||||
|
|
||||||
|
if (const ValueType* v = GetMember(value, "$ref"))
|
||||||
|
document->AddRefSchema(this, *v);
|
||||||
|
|
||||||
// Object
|
// Object
|
||||||
|
|
||||||
const ValueType* properties = GetMember(value, "properties");
|
const ValueType* properties = GetMember(value, "properties");
|
||||||
@ -456,7 +464,10 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return !not_ || !context.notValidator->IsValid();
|
if (not_ && context.notValidator->IsValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return !ref_ || context.refValidator->IsValid();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Null(Context& context) const {
|
bool Null(Context& context) const {
|
||||||
@ -741,6 +752,8 @@ private:
|
|||||||
if (oneOf_.schemas) CreateSchemaValidators(context, context.oneOfValidators, oneOf_);
|
if (oneOf_.schemas) CreateSchemaValidators(context, context.oneOfValidators, oneOf_);
|
||||||
if (not_ && !context.notValidator)
|
if (not_ && !context.notValidator)
|
||||||
context.notValidator = context.factory->CreateSchemaValidator(*not_);
|
context.notValidator = context.factory->CreateSchemaValidator(*not_);
|
||||||
|
if (ref_ && !context.refValidator)
|
||||||
|
context.refValidator = context.factory->CreateSchemaValidator(*ref_);
|
||||||
|
|
||||||
if (hasSchemaDependencies_ && !context.dependencyValidators.validators) {
|
if (hasSchemaDependencies_ && !context.dependencyValidators.validators) {
|
||||||
context.dependencyValidators.validators = new ISchemaValidator*[propertyCount_];
|
context.dependencyValidators.validators = new ISchemaValidator*[propertyCount_];
|
||||||
@ -817,6 +830,7 @@ private:
|
|||||||
SchemaArrayType anyOf_;
|
SchemaArrayType anyOf_;
|
||||||
SchemaArrayType oneOf_;
|
SchemaArrayType oneOf_;
|
||||||
SchemaType* not_;
|
SchemaType* not_;
|
||||||
|
SchemaType* ref_;
|
||||||
unsigned type_; // bitmask of kSchemaType
|
unsigned type_; // bitmask of kSchemaType
|
||||||
|
|
||||||
Property* properties_;
|
Property* properties_;
|
||||||
@ -858,18 +872,44 @@ public:
|
|||||||
friend class Schema<Encoding, Allocator>;
|
friend class Schema<Encoding, Allocator>;
|
||||||
|
|
||||||
template <typename DocumentType>
|
template <typename DocumentType>
|
||||||
GenericSchemaDocument(const DocumentType& document, Allocator* allocator = 0) : root_(), schemas_(), schemaCount_(), schemaMap_(allocator, kInitialSchemaMapSize) {
|
GenericSchemaDocument(const DocumentType& document, Allocator* allocator = 0) : root_(), schemas_(), schemaCount_(), schemaMap_(allocator, kInitialSchemaMapSize), schemaRef_(allocator, kInitialSchemaRefSize) {
|
||||||
typedef typename DocumentType::ValueType ValueType;
|
typedef typename DocumentType::ValueType ValueType;
|
||||||
|
typedef SchemaEntry<ValueType> SchemaEntryType;
|
||||||
|
typedef GenericPointer<ValueType> PointerType;
|
||||||
|
|
||||||
root_ = CreateSchema(GenericPointer<ValueType>(), static_cast<const ValueType&>(document));
|
// Generate root schema, it will call CreateSchema() to create sub-schemas,
|
||||||
|
// And call AddRefSchema() if there are $ref.
|
||||||
|
root_ = CreateSchema(PointerType(), static_cast<const ValueType&>(document));
|
||||||
|
|
||||||
// Copy to schemas and destroy the map
|
// Resolve $ref
|
||||||
|
while (!schemaRef_.Empty()) {
|
||||||
|
SchemaEntryType* refEntry = schemaRef_.template Pop<SchemaEntryType>(1);
|
||||||
|
PointerType p = refEntry->pointer; // Due to re-entrance,
|
||||||
|
SchemaType* source = refEntry->schema; // backup the entry first,
|
||||||
|
refEntry->~SchemaEntryType(); // and then destruct it.
|
||||||
|
|
||||||
|
bool resolved = false;
|
||||||
|
for (SchemaEntryType* target = schemaMap_.template Bottom<SchemaEntryType>(); target <= schemaMap_.template Top<SchemaEntryType>(); ++target)
|
||||||
|
if (p == target->pointer) {
|
||||||
|
source->ref_ = target->schema;
|
||||||
|
resolved = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not reesolved to existing schemas, try to create schema from the pointer
|
||||||
|
if (!resolved) {
|
||||||
|
if (const ValueType* v = p.Get(document))
|
||||||
|
source->ref_ = CreateSchema(p, *v); // cause re-entrance (modifying schemaRef_)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy to schemas_ and destroy schemaMap_ entries.
|
||||||
schemas_ = new SchemaType*[schemaCount_];
|
schemas_ = new SchemaType*[schemaCount_];
|
||||||
size_t i = schemaCount_;
|
size_t i = schemaCount_;
|
||||||
while (!schemaMap_.Empty()) {
|
while (!schemaMap_.Empty()) {
|
||||||
SchemaEntry<ValueType>* e = schemaMap_.template Pop<SchemaEntry<ValueType> > (1);
|
SchemaEntryType* e = schemaMap_.template Pop<SchemaEntryType>(1);
|
||||||
schemas_[--i] = e->schema;
|
schemas_[--i] = e->schema;
|
||||||
e->~SchemaEntry<ValueType>();
|
e->~SchemaEntryType();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -891,18 +931,30 @@ private:
|
|||||||
|
|
||||||
template <typename ValueType>
|
template <typename ValueType>
|
||||||
SchemaType* CreateSchema(const GenericPointer<ValueType>& pointer, const ValueType& v) {
|
SchemaType* CreateSchema(const GenericPointer<ValueType>& pointer, const ValueType& v) {
|
||||||
|
RAPIDJSON_ASSERT(pointer.IsValid());
|
||||||
SchemaType* schema = new SchemaType(this, pointer, v);
|
SchemaType* schema = new SchemaType(this, pointer, v);
|
||||||
new (schemaMap_.template Push<SchemaEntry<ValueType> >()) SchemaEntry<ValueType>(pointer, schema);
|
new (schemaMap_.template Push<SchemaEntry<ValueType> >()) SchemaEntry<ValueType>(pointer, schema);
|
||||||
schemaCount_++;
|
schemaCount_++;
|
||||||
return schema;
|
return schema;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const size_t kInitialSchemaMapSize = 1024;
|
template <typename ValueType>
|
||||||
|
void AddRefSchema(SchemaType* schema, const ValueType& v) {
|
||||||
|
if (v.IsString()) {
|
||||||
|
GenericPointer<ValueType> pointer(v.GetString(), v.GetStringLength());
|
||||||
|
if (pointer.IsValid())
|
||||||
|
new (schemaRef_.template Push<SchemaEntry<ValueType> >()) SchemaEntry<ValueType>(pointer, schema);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SchemaType* root_;
|
static const size_t kInitialSchemaMapSize = 1024;
|
||||||
SchemaType** schemas_;
|
static const size_t kInitialSchemaRefSize = 1024;
|
||||||
size_t schemaCount_;
|
|
||||||
internal::Stack<Allocator> schemaMap_; // Stores SchemaEntry<ValueType>
|
SchemaType* root_; //!< Root schema.
|
||||||
|
SchemaType** schemas_; //!< ALl schemas are owned by SchemaDocument
|
||||||
|
size_t schemaCount_; //!< Number of schemas owned
|
||||||
|
internal::Stack<Allocator> schemaMap_; // Stores created Pointer -> Schemas
|
||||||
|
internal::Stack<Allocator> schemaRef_; // Stores Pointer from $ref and schema which holds the $ref
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef GenericSchemaDocument<UTF8<> > SchemaDocument;
|
typedef GenericSchemaDocument<UTF8<> > SchemaDocument;
|
||||||
@ -974,6 +1026,8 @@ public:
|
|||||||
static_cast<GenericSchemaValidator*>(context->oneOfValidators.validators[i_])->method arg2;\
|
static_cast<GenericSchemaValidator*>(context->oneOfValidators.validators[i_])->method arg2;\
|
||||||
if (context->notValidator)\
|
if (context->notValidator)\
|
||||||
static_cast<GenericSchemaValidator*>(context->notValidator)->method arg2;\
|
static_cast<GenericSchemaValidator*>(context->notValidator)->method arg2;\
|
||||||
|
if (context->refValidator)\
|
||||||
|
static_cast<GenericSchemaValidator*>(context->refValidator)->method arg2;\
|
||||||
if (context->dependencyValidators.validators)\
|
if (context->dependencyValidators.validators)\
|
||||||
for (SizeType i_ = 0; i_ < context->dependencyValidators.count; i_++)\
|
for (SizeType i_ = 0; i_ < context->dependencyValidators.count; i_++)\
|
||||||
if (context->dependencyValidators.validators[i_])\
|
if (context->dependencyValidators.validators[i_])\
|
||||||
|
@ -129,6 +129,71 @@ TEST(SchemaValidator, Not) {
|
|||||||
VALIDATE(s, "\"I am a string\"", false);
|
VALIDATE(s, "\"I am a string\"", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(SchemaValidator, Ref) {
|
||||||
|
Document sd;
|
||||||
|
sd.Parse(
|
||||||
|
"{"
|
||||||
|
" \"$schema\": \"http://json-schema.org/draft-04/schema#\","
|
||||||
|
""
|
||||||
|
" \"definitions\": {"
|
||||||
|
" \"address\": {"
|
||||||
|
" \"type\": \"object\","
|
||||||
|
" \"properties\": {"
|
||||||
|
" \"street_address\": { \"type\": \"string\" },"
|
||||||
|
" \"city\": { \"type\": \"string\" },"
|
||||||
|
" \"state\": { \"type\": \"string\" }"
|
||||||
|
" },"
|
||||||
|
" \"required\": [\"street_address\", \"city\", \"state\"]"
|
||||||
|
" }"
|
||||||
|
" },"
|
||||||
|
" \"type\": \"object\","
|
||||||
|
" \"properties\": {"
|
||||||
|
" \"billing_address\": { \"$ref\": \"#/definitions/address\" },"
|
||||||
|
" \"shipping_address\": { \"$ref\": \"#/definitions/address\" }"
|
||||||
|
" }"
|
||||||
|
"}");
|
||||||
|
SchemaDocument s(sd);
|
||||||
|
|
||||||
|
VALIDATE(s, "{\"shipping_address\": {\"street_address\": \"1600 Pennsylvania Avenue NW\", \"city\": \"Washington\", \"state\": \"DC\"}, \"billing_address\": {\"street_address\": \"1st Street SE\", \"city\": \"Washington\", \"state\": \"DC\"} }", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SchemaValidator, Ref_AllOf) {
|
||||||
|
Document sd;
|
||||||
|
sd.Parse(
|
||||||
|
"{"
|
||||||
|
" \"$schema\": \"http://json-schema.org/draft-04/schema#\","
|
||||||
|
""
|
||||||
|
" \"definitions\": {"
|
||||||
|
" \"address\": {"
|
||||||
|
" \"type\": \"object\","
|
||||||
|
" \"properties\": {"
|
||||||
|
" \"street_address\": { \"type\": \"string\" },"
|
||||||
|
" \"city\": { \"type\": \"string\" },"
|
||||||
|
" \"state\": { \"type\": \"string\" }"
|
||||||
|
" },"
|
||||||
|
" \"required\": [\"street_address\", \"city\", \"state\"]"
|
||||||
|
" }"
|
||||||
|
" },"
|
||||||
|
" \"type\": \"object\","
|
||||||
|
" \"properties\": {"
|
||||||
|
" \"billing_address\": { \"$ref\": \"#/definitions/address\" },"
|
||||||
|
" \"shipping_address\": {"
|
||||||
|
" \"allOf\": ["
|
||||||
|
" { \"$ref\": \"#/definitions/address\" },"
|
||||||
|
" { \"properties\":"
|
||||||
|
" { \"type\": { \"enum\": [ \"residential\", \"business\" ] } },"
|
||||||
|
" \"required\": [\"type\"]"
|
||||||
|
" }"
|
||||||
|
" ]"
|
||||||
|
" }"
|
||||||
|
" }"
|
||||||
|
"}");
|
||||||
|
SchemaDocument s(sd);
|
||||||
|
|
||||||
|
VALIDATE(s, "{\"shipping_address\": {\"street_address\": \"1600 Pennsylvania Avenue NW\", \"city\": \"Washington\", \"state\": \"DC\"} }", false);
|
||||||
|
VALIDATE(s, "{\"shipping_address\": {\"street_address\": \"1600 Pennsylvania Avenue NW\", \"city\": \"Washington\", \"state\": \"DC\", \"type\": \"business\"} }", true);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(SchemaValidator, String) {
|
TEST(SchemaValidator, String) {
|
||||||
Document sd;
|
Document sd;
|
||||||
sd.Parse("{\"type\":\"string\"}");
|
sd.Parse("{\"type\":\"string\"}");
|
||||||
@ -669,7 +734,7 @@ TEST(SchemaValidator, TestSuite) {
|
|||||||
"additionalProperties.json",
|
"additionalProperties.json",
|
||||||
"allOf.json",
|
"allOf.json",
|
||||||
"anyOf.json",
|
"anyOf.json",
|
||||||
//"definitions.json",
|
"definitions.json",
|
||||||
"dependencies.json",
|
"dependencies.json",
|
||||||
"enum.json",
|
"enum.json",
|
||||||
"items.json",
|
"items.json",
|
||||||
@ -687,7 +752,7 @@ TEST(SchemaValidator, TestSuite) {
|
|||||||
"pattern.json",
|
"pattern.json",
|
||||||
"patternProperties.json",
|
"patternProperties.json",
|
||||||
"properties.json",
|
"properties.json",
|
||||||
//"ref.json",
|
"ref.json",
|
||||||
//"refRemote.json",
|
//"refRemote.json",
|
||||||
"required.json",
|
"required.json",
|
||||||
"type.json",
|
"type.json",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user