mirror of
https://github.com/tristanpenman/valijson.git
synced 2025-03-03 04:38:40 +01:00
Update TypeConstraint to use custom allocator for internal list of sub-schemas
This commit is contained in:
parent
000a1efc9a
commit
00e5c051f1
@ -114,8 +114,9 @@ void addPropertiesConstraint(Schema &schema)
|
|||||||
// Create a child schema for the 'description' property that requires
|
// Create a child schema for the 'description' property that requires
|
||||||
// a string, but does not enforce any length constraints.
|
// a string, but does not enforce any length constraints.
|
||||||
const Subschema *subschema = schema.createSubschema();
|
const Subschema *subschema = schema.createSubschema();
|
||||||
schema.addConstraintToSubschema(TypeConstraint(TypeConstraint::kString),
|
TypeConstraint typeConstraint;
|
||||||
subschema);
|
typeConstraint.addNamedType(TypeConstraint::kString);
|
||||||
|
schema.addConstraintToSubschema(typeConstraint, subschema);
|
||||||
|
|
||||||
// Include subschema in properties constraint
|
// Include subschema in properties constraint
|
||||||
propertySchemaMap["description"] = subschema;
|
propertySchemaMap["description"] = subschema;
|
||||||
@ -126,8 +127,9 @@ void addPropertiesConstraint(Schema &schema)
|
|||||||
// number with a value greater than zero.
|
// number with a value greater than zero.
|
||||||
const Subschema *subschema = schema.createSubschema();
|
const Subschema *subschema = schema.createSubschema();
|
||||||
schema.addConstraintToSubschema(MinimumConstraint(0.0, true), subschema);
|
schema.addConstraintToSubschema(MinimumConstraint(0.0, true), subschema);
|
||||||
schema.addConstraintToSubschema(TypeConstraint(TypeConstraint::kNumber),
|
TypeConstraint typeConstraint;
|
||||||
subschema);
|
typeConstraint.addNamedType(TypeConstraint::kNumber);
|
||||||
|
schema.addConstraintToSubschema(typeConstraint, subschema);
|
||||||
|
|
||||||
// Include subschema in properties constraint
|
// Include subschema in properties constraint
|
||||||
propertySchemaMap["price"] = subschema;
|
propertySchemaMap["price"] = subschema;
|
||||||
@ -139,8 +141,9 @@ void addPropertiesConstraint(Schema &schema)
|
|||||||
const Subschema *subschema = schema.createSubschema();
|
const Subschema *subschema = schema.createSubschema();
|
||||||
schema.addConstraintToSubschema(MaxLengthConstraint(200), subschema);
|
schema.addConstraintToSubschema(MaxLengthConstraint(200), subschema);
|
||||||
schema.addConstraintToSubschema(MinLengthConstraint(1), subschema);
|
schema.addConstraintToSubschema(MinLengthConstraint(1), subschema);
|
||||||
schema.addConstraintToSubschema(TypeConstraint(TypeConstraint::kString),
|
TypeConstraint typeConstraint;
|
||||||
subschema);
|
typeConstraint.addNamedType(TypeConstraint::kString);
|
||||||
|
schema.addConstraintToSubschema(typeConstraint, subschema);
|
||||||
|
|
||||||
// Include subschema in properties constraint
|
// Include subschema in properties constraint
|
||||||
propertySchemaMap["title"] = subschema;
|
propertySchemaMap["title"] = subschema;
|
||||||
@ -166,7 +169,9 @@ void addTypeConstraint(Schema &schema)
|
|||||||
{
|
{
|
||||||
// Add a TypeConstraint to the schema, specifying that the root of the
|
// Add a TypeConstraint to the schema, specifying that the root of the
|
||||||
// document must be an object.
|
// document must be an object.
|
||||||
schema.addConstraint(TypeConstraint(TypeConstraint::kObject));
|
TypeConstraint typeConstraint;
|
||||||
|
typeConstraint.addNamedType(TypeConstraint::kObject);
|
||||||
|
schema.addConstraint(typeConstraint);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
|
@ -602,8 +602,9 @@ private:
|
|||||||
/**
|
/**
|
||||||
* @brief Represents a 'type' constraint.
|
* @brief Represents a 'type' constraint.
|
||||||
*/
|
*/
|
||||||
struct TypeConstraint: BasicConstraint<TypeConstraint>
|
class TypeConstraint: public BasicConstraint<TypeConstraint>
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
enum JsonType {
|
enum JsonType {
|
||||||
kAny,
|
kAny,
|
||||||
kArray,
|
kArray,
|
||||||
@ -615,29 +616,51 @@ struct TypeConstraint: BasicConstraint<TypeConstraint>
|
|||||||
kString
|
kString
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::set<JsonType> JsonTypes;
|
TypeConstraint()
|
||||||
|
: namedTypes(std::less<JsonType>(), allocator),
|
||||||
|
schemaTypes(Allocator::rebind<const Subschema *>::other(allocator)) { }
|
||||||
|
|
||||||
typedef std::vector<const Subschema *> Schemas;
|
TypeConstraint(CustomAlloc allocFn, CustomFree freeFn)
|
||||||
|
: BasicConstraint(allocFn, freeFn),
|
||||||
|
namedTypes(std::less<JsonType>(), allocator),
|
||||||
|
schemaTypes(Allocator::rebind<const Subschema *>::other(allocator)) { }
|
||||||
|
|
||||||
TypeConstraint(const JsonType jsonType)
|
void addNamedType(JsonType type)
|
||||||
: jsonTypes(makeJsonTypes(jsonType)) { }
|
|
||||||
|
|
||||||
TypeConstraint(const JsonTypes &jsonTypes)
|
|
||||||
: jsonTypes(jsonTypes) { }
|
|
||||||
|
|
||||||
TypeConstraint(const JsonTypes &jsonTypes,
|
|
||||||
const Schemas &schemas)
|
|
||||||
: jsonTypes(jsonTypes),
|
|
||||||
schemas(schemas) { }
|
|
||||||
|
|
||||||
static JsonTypes makeJsonTypes(const JsonType jsonType)
|
|
||||||
{
|
{
|
||||||
JsonTypes jsonTypes;
|
namedTypes.insert(type);
|
||||||
jsonTypes.insert(jsonType);
|
|
||||||
return jsonTypes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static JsonType jsonTypeFromString(const std::string &typeName)
|
void addSchemaType(const Subschema *subschema)
|
||||||
|
{
|
||||||
|
schemaTypes.push_back(subschema);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename FunctorType>
|
||||||
|
void applyToNamedTypes(const FunctorType &fn) const
|
||||||
|
{
|
||||||
|
BOOST_FOREACH( const JsonType namedType, namedTypes ) {
|
||||||
|
if (!fn(namedType)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename FunctorType>
|
||||||
|
void applyToSchemaTypes(const FunctorType &fn) const
|
||||||
|
{
|
||||||
|
unsigned int index = 0;
|
||||||
|
BOOST_FOREACH( const Subschema *subschema, schemaTypes ) {
|
||||||
|
if (!fn(index, subschema)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename AllocatorType>
|
||||||
|
static JsonType jsonTypeFromString(const std::basic_string<char,
|
||||||
|
std::char_traits<char>, AllocatorType> &typeName)
|
||||||
{
|
{
|
||||||
if (typeName.compare("any") == 0) {
|
if (typeName.compare("any") == 0) {
|
||||||
return kAny;
|
return kAny;
|
||||||
@ -657,11 +680,21 @@ struct TypeConstraint: BasicConstraint<TypeConstraint>
|
|||||||
return kString;
|
return kString;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw std::runtime_error("Unrecognised JSON type name '" + typeName + "'");
|
throw std::runtime_error("Unrecognised JSON type name '" +
|
||||||
|
std::string(typeName.c_str()) + "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
const JsonTypes jsonTypes;
|
private:
|
||||||
const Schemas schemas;
|
typedef std::set<JsonType, std::less<JsonType>, Allocator> NamedTypes;
|
||||||
|
|
||||||
|
typedef std::vector<const Subschema *,
|
||||||
|
Allocator::rebind<const Subschema *>::other> SchemaTypes;
|
||||||
|
|
||||||
|
/// Set of named JSON types that serve as valid types
|
||||||
|
NamedTypes namedTypes;
|
||||||
|
|
||||||
|
/// Set of sub-schemas that serve as valid types
|
||||||
|
SchemaTypes schemaTypes;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -20,7 +20,6 @@ struct NotConstraint;
|
|||||||
struct PatternConstraint;
|
struct PatternConstraint;
|
||||||
struct PropertiesConstraint;
|
struct PropertiesConstraint;
|
||||||
struct RequiredConstraint;
|
struct RequiredConstraint;
|
||||||
struct TypeConstraint;
|
|
||||||
struct UniqueItemsConstraint;
|
struct UniqueItemsConstraint;
|
||||||
|
|
||||||
class AllOfConstraint;
|
class AllOfConstraint;
|
||||||
@ -29,6 +28,7 @@ class DependenciesConstraint;
|
|||||||
class LinearItemsConstraint;
|
class LinearItemsConstraint;
|
||||||
class OneOfConstraint;
|
class OneOfConstraint;
|
||||||
class SingularItemsConstraint;
|
class SingularItemsConstraint;
|
||||||
|
class TypeConstraint;
|
||||||
|
|
||||||
/// Interface to allow usage of the visitor pattern with Constraints
|
/// Interface to allow usage of the visitor pattern with Constraints
|
||||||
class ConstraintVisitor
|
class ConstraintVisitor
|
||||||
|
@ -1432,52 +1432,62 @@ private:
|
|||||||
const boost::optional<
|
const boost::optional<
|
||||||
typename FetchDocumentFunction<AdapterType>::Type > fetchDoc)
|
typename FetchDocumentFunction<AdapterType>::Type > fetchDoc)
|
||||||
{
|
{
|
||||||
typedef constraints::TypeConstraint TC;
|
typedef constraints::TypeConstraint TypeConstraint;
|
||||||
|
|
||||||
TC::JsonTypes jsonTypes;
|
TypeConstraint constraint;
|
||||||
TC::Schemas schemas;
|
|
||||||
|
|
||||||
if (node.isString()) {
|
if (node.isString()) {
|
||||||
const TC::JsonType jsonType =
|
const TypeConstraint::JsonType type =
|
||||||
TC::jsonTypeFromString(node.getString());
|
TypeConstraint::jsonTypeFromString(node.getString());
|
||||||
if (jsonType == TC::kAny && version == kDraft4) {
|
|
||||||
|
if (type == TypeConstraint::kAny && version == kDraft4) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"'any' type is not supported in version 4 schemas.");
|
"'any' type is not supported in version 4 schemas.");
|
||||||
}
|
}
|
||||||
jsonTypes.insert(jsonType);
|
|
||||||
|
constraint.addNamedType(type);
|
||||||
|
|
||||||
} else if (node.isArray()) {
|
} else if (node.isArray()) {
|
||||||
int index = 0;
|
int index = 0;
|
||||||
BOOST_FOREACH( const AdapterType v, node.getArray() ) {
|
BOOST_FOREACH( const AdapterType v, node.getArray() ) {
|
||||||
if (v.isString()) {
|
if (v.isString()) {
|
||||||
const TC::JsonType jsonType =
|
const TypeConstraint::JsonType type =
|
||||||
TC::jsonTypeFromString(v.getString());
|
TypeConstraint::jsonTypeFromString(v.getString());
|
||||||
if (jsonType == TC::kAny && version == kDraft4) {
|
|
||||||
|
if (type == TypeConstraint::kAny && version == kDraft4) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"'any' type is not supported in version 4 "
|
"'any' type is not supported in version 4 "
|
||||||
"schemas.");
|
"schemas.");
|
||||||
}
|
}
|
||||||
jsonTypes.insert(jsonType);
|
|
||||||
|
constraint.addNamedType(type);
|
||||||
|
|
||||||
} else if (v.isObject() && version == kDraft3) {
|
} else if (v.isObject() && version == kDraft3) {
|
||||||
const std::string childPath = nodePath + "/" +
|
const std::string childPath = nodePath + "/" +
|
||||||
boost::lexical_cast<std::string>(index);
|
boost::lexical_cast<std::string>(index);
|
||||||
schemas.push_back(rootSchema.createSubschema());
|
const Subschema *subschema = rootSchema.createSubschema();
|
||||||
|
constraint.addSchemaType(subschema);
|
||||||
populateSchema<AdapterType>(rootSchema, rootNode, v,
|
populateSchema<AdapterType>(rootSchema, rootNode, v,
|
||||||
*schemas.back(), currentScope, childPath, fetchDoc);
|
*subschema, currentScope, childPath, fetchDoc);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
throw std::runtime_error("Type name should be a string.");
|
throw std::runtime_error("Type name should be a string.");
|
||||||
}
|
}
|
||||||
|
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (node.isObject() && version == kDraft3) {
|
} else if (node.isObject() && version == kDraft3) {
|
||||||
schemas.push_back(rootSchema.createSubschema());
|
const Subschema *subschema = rootSchema.createSubschema();
|
||||||
|
constraint.addSchemaType(subschema);
|
||||||
populateSchema<AdapterType>(rootSchema, rootNode, node,
|
populateSchema<AdapterType>(rootSchema, rootNode, node,
|
||||||
*schemas.back(), currentScope, nodePath, fetchDoc);
|
*subschema, currentScope, nodePath, fetchDoc);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
throw std::runtime_error("Type name should be a string.");
|
throw std::runtime_error("Type name should be a string.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return constraints::TypeConstraint(jsonTypes, schemas);
|
return constraint;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -968,68 +968,39 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Validate against the type constraint represented by a
|
* @brief Validate a value against a TypeConstraint
|
||||||
* TypeConstraint object.
|
|
||||||
*
|
*
|
||||||
* Checks that the target is of the expected type.
|
* Checks that the target is one of the valid named types, or matches one
|
||||||
|
* of a set of valid sub-schemas.
|
||||||
*
|
*
|
||||||
* @param constraint Constraint that the target must validate against
|
* @param constraint TypeConstraint to validate against
|
||||||
*
|
*
|
||||||
* @return true if validation succeeds, false otherwise
|
* @return \c true if validation is successful; \c false otherwise
|
||||||
*/
|
*/
|
||||||
virtual bool visit(const TypeConstraint &constraint)
|
virtual bool visit(const TypeConstraint &constraint)
|
||||||
{
|
{
|
||||||
// Try to match the type to one of the types in the jsonTypes array
|
// Check named types
|
||||||
BOOST_FOREACH( const TypeConstraint::JsonType jsonType, constraint.jsonTypes ) {
|
{
|
||||||
switch (jsonType) {
|
// ValidateNamedTypes functor assumes target is invalid
|
||||||
case TypeConstraint::kAny:
|
bool validated = false;
|
||||||
return true;
|
constraint.applyToNamedTypes(ValidateNamedTypes(target, strictTypes,
|
||||||
case TypeConstraint::kArray:
|
false, true, &validated));
|
||||||
if (target.isArray()) {
|
if (validated) {
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TypeConstraint::kBoolean:
|
|
||||||
if (target.isBool() || (!strictTypes && target.maybeBool())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TypeConstraint::kInteger:
|
|
||||||
if (target.isInteger() || (!strictTypes && target.maybeInteger())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TypeConstraint::kNull:
|
|
||||||
if (target.isNull() || (!strictTypes && target.maybeNull())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TypeConstraint::kNumber:
|
|
||||||
if (target.isNumber() || (!strictTypes && target.maybeDouble())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TypeConstraint::kObject:
|
|
||||||
if (target.isObject()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TypeConstraint::kString:
|
|
||||||
if (target.isString()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_FOREACH( const Subschema *subschema, constraint.schemas ) {
|
|
||||||
if (validateSchema(*subschema)) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (results) {
|
// Check schema-based types
|
||||||
results->pushError(context, "Value type not permitted by 'type' constraint.");
|
{
|
||||||
|
unsigned int numValidated = 0;
|
||||||
|
constraint.applyToSchemaTypes(ValidateSubschemas(target, context,
|
||||||
|
false, true, *this, NULL, &numValidated, NULL));
|
||||||
|
if (numValidated > 0) {
|
||||||
|
return true;
|
||||||
|
} else if (results) {
|
||||||
|
results->pushError(context,
|
||||||
|
"Value type not permitted by 'type' constraint.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -1205,6 +1176,74 @@ private:
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Functor to validate value against named JSON types
|
||||||
|
*/
|
||||||
|
struct ValidateNamedTypes
|
||||||
|
{
|
||||||
|
ValidateNamedTypes(
|
||||||
|
const AdapterType &target,
|
||||||
|
bool strict,
|
||||||
|
bool continueOnSuccess,
|
||||||
|
bool continueOnFailure,
|
||||||
|
bool *validated)
|
||||||
|
: target(target),
|
||||||
|
strict(strict),
|
||||||
|
continueOnSuccess(continueOnSuccess),
|
||||||
|
continueOnFailure(continueOnFailure),
|
||||||
|
validated(validated) { }
|
||||||
|
|
||||||
|
bool operator()(constraints::TypeConstraint::JsonType jsonType) const
|
||||||
|
{
|
||||||
|
typedef constraints::TypeConstraint TypeConstraint;
|
||||||
|
|
||||||
|
bool valid = false;
|
||||||
|
|
||||||
|
switch (jsonType) {
|
||||||
|
case TypeConstraint::kAny:
|
||||||
|
valid = true;
|
||||||
|
break;
|
||||||
|
case TypeConstraint::kArray:
|
||||||
|
valid = target.isArray();
|
||||||
|
break;
|
||||||
|
case TypeConstraint::kBoolean:
|
||||||
|
valid = target.isBool() || (!strict && target.maybeBool());
|
||||||
|
break;
|
||||||
|
case TypeConstraint::kInteger:
|
||||||
|
valid = target.isInteger() ||
|
||||||
|
(!strict && target.maybeInteger());
|
||||||
|
break;
|
||||||
|
case TypeConstraint::kNull:
|
||||||
|
valid = target.isNull() || (!strict && target.maybeNull());
|
||||||
|
break;
|
||||||
|
case TypeConstraint::kNumber:
|
||||||
|
valid = target.isNumber() || (!strict && target.maybeDouble());
|
||||||
|
break;
|
||||||
|
case TypeConstraint::kObject:
|
||||||
|
valid = target.isObject();
|
||||||
|
break;
|
||||||
|
case TypeConstraint::kString:
|
||||||
|
valid = target.isString();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valid && validated) {
|
||||||
|
*validated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (valid && continueOnSuccess) || continueOnFailure;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const AdapterType target;
|
||||||
|
const bool strict;
|
||||||
|
const bool continueOnSuccess;
|
||||||
|
const bool continueOnFailure;
|
||||||
|
bool * const validated;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Functor to validate schema-based dependencies
|
* @brief Functor to validate schema-based dependencies
|
||||||
*/
|
*/
|
||||||
@ -1306,8 +1345,8 @@ private:
|
|||||||
|
|
||||||
if (results) {
|
if (results) {
|
||||||
results->pushError(context,
|
results->pushError(context,
|
||||||
"Failed to validate against child schema #" +
|
"Failed to validate against child schema #" +
|
||||||
boost::lexical_cast<std::string>(index) + ".");
|
boost::lexical_cast<std::string>(index) + ".");
|
||||||
}
|
}
|
||||||
|
|
||||||
return continueOnFailure;
|
return continueOnFailure;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user