mirror of
https://github.com/tristanpenman/valijson.git
synced 2025-03-02 20:30:11 +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
|
||||
// a string, but does not enforce any length constraints.
|
||||
const Subschema *subschema = schema.createSubschema();
|
||||
schema.addConstraintToSubschema(TypeConstraint(TypeConstraint::kString),
|
||||
subschema);
|
||||
TypeConstraint typeConstraint;
|
||||
typeConstraint.addNamedType(TypeConstraint::kString);
|
||||
schema.addConstraintToSubschema(typeConstraint, subschema);
|
||||
|
||||
// Include subschema in properties constraint
|
||||
propertySchemaMap["description"] = subschema;
|
||||
@ -126,8 +127,9 @@ void addPropertiesConstraint(Schema &schema)
|
||||
// number with a value greater than zero.
|
||||
const Subschema *subschema = schema.createSubschema();
|
||||
schema.addConstraintToSubschema(MinimumConstraint(0.0, true), subschema);
|
||||
schema.addConstraintToSubschema(TypeConstraint(TypeConstraint::kNumber),
|
||||
subschema);
|
||||
TypeConstraint typeConstraint;
|
||||
typeConstraint.addNamedType(TypeConstraint::kNumber);
|
||||
schema.addConstraintToSubschema(typeConstraint, subschema);
|
||||
|
||||
// Include subschema in properties constraint
|
||||
propertySchemaMap["price"] = subschema;
|
||||
@ -139,8 +141,9 @@ void addPropertiesConstraint(Schema &schema)
|
||||
const Subschema *subschema = schema.createSubschema();
|
||||
schema.addConstraintToSubschema(MaxLengthConstraint(200), subschema);
|
||||
schema.addConstraintToSubschema(MinLengthConstraint(1), subschema);
|
||||
schema.addConstraintToSubschema(TypeConstraint(TypeConstraint::kString),
|
||||
subschema);
|
||||
TypeConstraint typeConstraint;
|
||||
typeConstraint.addNamedType(TypeConstraint::kString);
|
||||
schema.addConstraintToSubschema(typeConstraint, subschema);
|
||||
|
||||
// Include subschema in properties constraint
|
||||
propertySchemaMap["title"] = subschema;
|
||||
@ -166,7 +169,9 @@ void addTypeConstraint(Schema &schema)
|
||||
{
|
||||
// Add a TypeConstraint to the schema, specifying that the root of the
|
||||
// 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[])
|
||||
|
@ -602,8 +602,9 @@ private:
|
||||
/**
|
||||
* @brief Represents a 'type' constraint.
|
||||
*/
|
||||
struct TypeConstraint: BasicConstraint<TypeConstraint>
|
||||
class TypeConstraint: public BasicConstraint<TypeConstraint>
|
||||
{
|
||||
public:
|
||||
enum JsonType {
|
||||
kAny,
|
||||
kArray,
|
||||
@ -615,29 +616,51 @@ struct TypeConstraint: BasicConstraint<TypeConstraint>
|
||||
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)
|
||||
: 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)
|
||||
void addNamedType(JsonType type)
|
||||
{
|
||||
JsonTypes jsonTypes;
|
||||
jsonTypes.insert(jsonType);
|
||||
return jsonTypes;
|
||||
namedTypes.insert(type);
|
||||
}
|
||||
|
||||
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) {
|
||||
return kAny;
|
||||
@ -657,11 +680,21 @@ struct TypeConstraint: BasicConstraint<TypeConstraint>
|
||||
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;
|
||||
const Schemas schemas;
|
||||
private:
|
||||
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 PropertiesConstraint;
|
||||
struct RequiredConstraint;
|
||||
struct TypeConstraint;
|
||||
struct UniqueItemsConstraint;
|
||||
|
||||
class AllOfConstraint;
|
||||
@ -29,6 +28,7 @@ class DependenciesConstraint;
|
||||
class LinearItemsConstraint;
|
||||
class OneOfConstraint;
|
||||
class SingularItemsConstraint;
|
||||
class TypeConstraint;
|
||||
|
||||
/// Interface to allow usage of the visitor pattern with Constraints
|
||||
class ConstraintVisitor
|
||||
|
@ -1432,52 +1432,62 @@ private:
|
||||
const boost::optional<
|
||||
typename FetchDocumentFunction<AdapterType>::Type > fetchDoc)
|
||||
{
|
||||
typedef constraints::TypeConstraint TC;
|
||||
typedef constraints::TypeConstraint TypeConstraint;
|
||||
|
||||
TC::JsonTypes jsonTypes;
|
||||
TC::Schemas schemas;
|
||||
TypeConstraint constraint;
|
||||
|
||||
if (node.isString()) {
|
||||
const TC::JsonType jsonType =
|
||||
TC::jsonTypeFromString(node.getString());
|
||||
if (jsonType == TC::kAny && version == kDraft4) {
|
||||
const TypeConstraint::JsonType type =
|
||||
TypeConstraint::jsonTypeFromString(node.getString());
|
||||
|
||||
if (type == TypeConstraint::kAny && version == kDraft4) {
|
||||
throw std::runtime_error(
|
||||
"'any' type is not supported in version 4 schemas.");
|
||||
}
|
||||
jsonTypes.insert(jsonType);
|
||||
|
||||
constraint.addNamedType(type);
|
||||
|
||||
} else if (node.isArray()) {
|
||||
int index = 0;
|
||||
BOOST_FOREACH( const AdapterType v, node.getArray() ) {
|
||||
if (v.isString()) {
|
||||
const TC::JsonType jsonType =
|
||||
TC::jsonTypeFromString(v.getString());
|
||||
if (jsonType == TC::kAny && version == kDraft4) {
|
||||
const TypeConstraint::JsonType type =
|
||||
TypeConstraint::jsonTypeFromString(v.getString());
|
||||
|
||||
if (type == TypeConstraint::kAny && version == kDraft4) {
|
||||
throw std::runtime_error(
|
||||
"'any' type is not supported in version 4 "
|
||||
"schemas.");
|
||||
}
|
||||
jsonTypes.insert(jsonType);
|
||||
|
||||
constraint.addNamedType(type);
|
||||
|
||||
} else if (v.isObject() && version == kDraft3) {
|
||||
const std::string childPath = nodePath + "/" +
|
||||
boost::lexical_cast<std::string>(index);
|
||||
schemas.push_back(rootSchema.createSubschema());
|
||||
const Subschema *subschema = rootSchema.createSubschema();
|
||||
constraint.addSchemaType(subschema);
|
||||
populateSchema<AdapterType>(rootSchema, rootNode, v,
|
||||
*schemas.back(), currentScope, childPath, fetchDoc);
|
||||
*subschema, currentScope, childPath, fetchDoc);
|
||||
|
||||
} else {
|
||||
throw std::runtime_error("Type name should be a string.");
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
} else if (node.isObject() && version == kDraft3) {
|
||||
schemas.push_back(rootSchema.createSubschema());
|
||||
const Subschema *subschema = rootSchema.createSubschema();
|
||||
constraint.addSchemaType(subschema);
|
||||
populateSchema<AdapterType>(rootSchema, rootNode, node,
|
||||
*schemas.back(), currentScope, nodePath, fetchDoc);
|
||||
*subschema, currentScope, nodePath, fetchDoc);
|
||||
|
||||
} else {
|
||||
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
|
||||
* TypeConstraint object.
|
||||
* @brief Validate a value against a TypeConstraint
|
||||
*
|
||||
* 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)
|
||||
{
|
||||
// Try to match the type to one of the types in the jsonTypes array
|
||||
BOOST_FOREACH( const TypeConstraint::JsonType jsonType, constraint.jsonTypes ) {
|
||||
switch (jsonType) {
|
||||
case TypeConstraint::kAny:
|
||||
return true;
|
||||
case TypeConstraint::kArray:
|
||||
if (target.isArray()) {
|
||||
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)) {
|
||||
// Check named types
|
||||
{
|
||||
// ValidateNamedTypes functor assumes target is invalid
|
||||
bool validated = false;
|
||||
constraint.applyToNamedTypes(ValidateNamedTypes(target, strictTypes,
|
||||
false, true, &validated));
|
||||
if (validated) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (results) {
|
||||
results->pushError(context, "Value type not permitted by 'type' constraint.");
|
||||
// Check schema-based types
|
||||
{
|
||||
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;
|
||||
@ -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
|
||||
*/
|
||||
@ -1306,8 +1345,8 @@ private:
|
||||
|
||||
if (results) {
|
||||
results->pushError(context,
|
||||
"Failed to validate against child schema #" +
|
||||
boost::lexical_cast<std::string>(index) + ".");
|
||||
"Failed to validate against child schema #" +
|
||||
boost::lexical_cast<std::string>(index) + ".");
|
||||
}
|
||||
|
||||
return continueOnFailure;
|
||||
|
Loading…
x
Reference in New Issue
Block a user