mirror of
https://github.com/tristanpenman/valijson.git
synced 2025-03-03 12:58:03 +01:00
Improve support for boolean subschemas
This commit is contained in:
parent
c89d1d89cd
commit
866f5761bf
@ -40,13 +40,11 @@ class AllOfConstraint: public BasicConstraint<AllOfConstraint>
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AllOfConstraint()
|
AllOfConstraint()
|
||||||
: subschemas(Allocator::rebind<const Subschema *>::other(allocator)),
|
: subschemas(Allocator::rebind<const Subschema *>::other(allocator)) { }
|
||||||
alwaysInvalid(false) { }
|
|
||||||
|
|
||||||
AllOfConstraint(CustomAlloc allocFn, CustomFree freeFn)
|
AllOfConstraint(CustomAlloc allocFn, CustomFree freeFn)
|
||||||
: BasicConstraint(allocFn, freeFn),
|
: BasicConstraint(allocFn, freeFn),
|
||||||
subschemas(Allocator::rebind<const Subschema *>::other(allocator)),
|
subschemas(Allocator::rebind<const Subschema *>::other(allocator)) { }
|
||||||
alwaysInvalid(false) { }
|
|
||||||
|
|
||||||
void addSubschema(const Subschema *subschema)
|
void addSubschema(const Subschema *subschema)
|
||||||
{
|
{
|
||||||
@ -66,24 +64,12 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool getAlwaysInvalid() const
|
|
||||||
{
|
|
||||||
return alwaysInvalid;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setAlwaysInvalid()
|
|
||||||
{
|
|
||||||
alwaysInvalid = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef std::vector<const Subschema *,
|
typedef std::vector<const Subschema *,
|
||||||
internal::CustomAllocator<const Subschema *> > Subschemas;
|
internal::CustomAllocator<const Subschema *> > Subschemas;
|
||||||
|
|
||||||
/// Collection of sub-schemas, all of which must be satisfied
|
/// Collection of sub-schemas, all of which must be satisfied
|
||||||
Subschemas subschemas;
|
Subschemas subschemas;
|
||||||
|
|
||||||
bool alwaysInvalid;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -97,8 +83,7 @@ class AnyOfConstraint: public BasicConstraint<AnyOfConstraint>
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AnyOfConstraint()
|
AnyOfConstraint()
|
||||||
: subschemas(Allocator::rebind<const Subschema *>::other(allocator)),
|
: subschemas(Allocator::rebind<const Subschema *>::other(allocator)) { }
|
||||||
alwaysValid(false) { }
|
|
||||||
|
|
||||||
AnyOfConstraint(CustomAlloc allocFn, CustomFree freeFn)
|
AnyOfConstraint(CustomAlloc allocFn, CustomFree freeFn)
|
||||||
: BasicConstraint(allocFn, freeFn),
|
: BasicConstraint(allocFn, freeFn),
|
||||||
@ -122,24 +107,12 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool getAlwaysValid() const
|
|
||||||
{
|
|
||||||
return alwaysValid;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setAlwaysValid()
|
|
||||||
{
|
|
||||||
alwaysValid = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef std::vector<const Subschema *,
|
typedef std::vector<const Subschema *,
|
||||||
internal::CustomAllocator<const Subschema *> > Subschemas;
|
internal::CustomAllocator<const Subschema *> > Subschemas;
|
||||||
|
|
||||||
/// Collection of sub-schemas, at least one of which must be satisfied
|
/// Collection of sub-schemas, at least one of which must be satisfied
|
||||||
Subschemas subschemas;
|
Subschemas subschemas;
|
||||||
|
|
||||||
bool alwaysValid;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class ConditionalConstraint: public BasicConstraint<ConditionalConstraint>
|
class ConditionalConstraint: public BasicConstraint<ConditionalConstraint>
|
||||||
|
@ -118,6 +118,11 @@ public:
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setAlwaysInvalid(const Subschema *subschema, bool value)
|
||||||
|
{
|
||||||
|
mutableSubschema(subschema)->setAlwaysInvalid(value);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Update the description for one of the sub-schemas owned by this
|
* @brief Update the description for one of the sub-schemas owned by this
|
||||||
* Schema instance
|
* Schema instance
|
||||||
|
@ -594,12 +594,24 @@ private:
|
|||||||
"appropriate Adapter implementation");
|
"appropriate Adapter implementation");
|
||||||
|
|
||||||
if (!node.isObject()) {
|
if (!node.isObject()) {
|
||||||
std::string s;
|
if (version == kDraft7 && node.maybeBool()) {
|
||||||
s += "Expected node at ";
|
// Boolean schema
|
||||||
s += nodePath;
|
if (!node.asBool()) {
|
||||||
s += " to contain schema object; actual node type is: ";
|
rootSchema.setAlwaysInvalid(&subschema, true);
|
||||||
s += internal::nodeTypeAsString(node);
|
}
|
||||||
throw std::runtime_error(s);
|
return;
|
||||||
|
} else {
|
||||||
|
std::string s;
|
||||||
|
s += "Expected node at ";
|
||||||
|
s += nodePath;
|
||||||
|
if (version == kDraft7) {
|
||||||
|
s += " to contain schema object or boolean value; actual node type is: ";
|
||||||
|
} else {
|
||||||
|
s += " to contain schema object; actual node type is: ";
|
||||||
|
}
|
||||||
|
s += internal::nodeTypeAsString(node);
|
||||||
|
throw std::runtime_error(s);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const typename AdapterType::Object object = node.asObject();
|
const typename AdapterType::Object object = node.asObject();
|
||||||
@ -1095,26 +1107,16 @@ private:
|
|||||||
|
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (const AdapterType schemaNode : node.asArray()) {
|
for (const AdapterType schemaNode : node.asArray()) {
|
||||||
if (schemaNode.maybeObject()) {
|
if (schemaNode.maybeObject() || (version == kDraft7 && schemaNode.isBool())) {
|
||||||
const std::string childPath = nodePath + "/" + std::to_string(index);
|
const std::string childPath = nodePath + "/" + std::to_string(index);
|
||||||
const Subschema *subschema = makeOrReuseSchema<AdapterType>(
|
const Subschema *subschema = makeOrReuseSchema<AdapterType>(
|
||||||
rootSchema, rootNode, schemaNode, currentScope,
|
rootSchema, rootNode, schemaNode, currentScope,
|
||||||
childPath, fetchDoc, NULL, NULL, docCache, schemaCache);
|
childPath, fetchDoc, NULL, NULL, docCache, schemaCache);
|
||||||
constraint.addSubschema(subschema);
|
constraint.addSubschema(subschema);
|
||||||
index++;
|
index++;
|
||||||
} else if (version == kDraft7) {
|
|
||||||
if (schemaNode.maybeBool()) {
|
|
||||||
if (!schemaNode.asBool()) {
|
|
||||||
// Schema that always fails
|
|
||||||
constraint.setAlwaysInvalid();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw std::runtime_error(
|
|
||||||
"Expected element to be an object or boolean value in 'allOf' constraint");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"Expected element to be an object value in 'allOf' constraint.");
|
"Expected element to be a valid schema in 'allOf' constraint.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1159,25 +1161,16 @@ private:
|
|||||||
|
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (const AdapterType schemaNode : node.asArray()) {
|
for (const AdapterType schemaNode : node.asArray()) {
|
||||||
if (schemaNode.maybeObject()) {
|
if (schemaNode.maybeObject() || (version == kDraft7 && schemaNode.isBool())) {
|
||||||
const std::string childPath = nodePath + "/" + std::to_string(index);
|
const std::string childPath = nodePath + "/" + std::to_string(index);
|
||||||
const Subschema *subschema = makeOrReuseSchema<AdapterType>(
|
const Subschema *subschema = makeOrReuseSchema<AdapterType>(
|
||||||
rootSchema, rootNode, schemaNode, currentScope,
|
rootSchema, rootNode, schemaNode, currentScope,
|
||||||
childPath, fetchDoc, NULL, NULL, docCache, schemaCache);
|
childPath, fetchDoc, NULL, NULL, docCache, schemaCache);
|
||||||
constraint.addSubschema(subschema);
|
constraint.addSubschema(subschema);
|
||||||
index++;
|
index++;
|
||||||
} else if (version == kDraft7) {
|
|
||||||
if (schemaNode.maybeBool()) {
|
|
||||||
if (schemaNode.asBool()) {
|
|
||||||
constraint.setAlwaysValid();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw std::runtime_error(
|
|
||||||
"Expected element to be an object or boolean value in 'allOf' constraint");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"Expected array element to be an object value in 'anyOf' constraint.");
|
"Expected array element to be a valid schema in 'anyOf' constraint.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1270,7 +1263,7 @@ private:
|
|||||||
SchemaCache &schemaCache)
|
SchemaCache &schemaCache)
|
||||||
{
|
{
|
||||||
if (!node.maybeObject()) {
|
if (!node.maybeObject()) {
|
||||||
throw std::runtime_error("Expected object value for 'dependencies' constraint.");
|
throw std::runtime_error("Expected valid subschema for 'dependencies' constraint.");
|
||||||
}
|
}
|
||||||
|
|
||||||
constraints::DependenciesConstraint dependenciesConstraint;
|
constraints::DependenciesConstraint dependenciesConstraint;
|
||||||
@ -1508,7 +1501,7 @@ private:
|
|||||||
// array is provided, or a single Schema object, in an object value is
|
// array is provided, or a single Schema object, in an object value is
|
||||||
// provided. If the items constraint is not provided, then array items
|
// provided. If the items constraint is not provided, then array items
|
||||||
// will be validated against the additionalItems schema.
|
// will be validated against the additionalItems schema.
|
||||||
if (items.isObject()) {
|
if (items.isObject() || (version == kDraft7 && items.maybeBool())) {
|
||||||
// If the items constraint contains an object value, then it
|
// If the items constraint contains an object value, then it
|
||||||
// should contain a Schema that will be used to validate all
|
// should contain a Schema that will be used to validate all
|
||||||
// items in a target array. Any schema defined by the
|
// items in a target array. Any schema defined by the
|
||||||
@ -1526,8 +1519,7 @@ private:
|
|||||||
} else {
|
} else {
|
||||||
// All other formats will result in an exception being thrown.
|
// All other formats will result in an exception being thrown.
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"Expected object value for singular 'items' "
|
"Expected valid schema for singular 'items' constraint.");
|
||||||
"constraint.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return constraint;
|
return constraint;
|
||||||
@ -1883,7 +1875,7 @@ private:
|
|||||||
typename DocumentCache<AdapterType>::Type &docCache,
|
typename DocumentCache<AdapterType>::Type &docCache,
|
||||||
SchemaCache &schemaCache)
|
SchemaCache &schemaCache)
|
||||||
{
|
{
|
||||||
if (node.maybeObject()) {
|
if (node.maybeObject() || (version == kDraft7 && node.maybeBool())) {
|
||||||
const Subschema *subschema = makeOrReuseSchema<AdapterType>(
|
const Subschema *subschema = makeOrReuseSchema<AdapterType>(
|
||||||
rootSchema, rootNode, node, currentScope, nodePath,
|
rootSchema, rootNode, node, currentScope, nodePath,
|
||||||
fetchDoc, NULL, NULL, docCache, schemaCache);
|
fetchDoc, NULL, NULL, docCache, schemaCache);
|
||||||
|
@ -40,7 +40,8 @@ public:
|
|||||||
*/
|
*/
|
||||||
Subschema()
|
Subschema()
|
||||||
: allocFn(::operator new)
|
: allocFn(::operator new)
|
||||||
, freeFn(::operator delete) { }
|
, freeFn(::operator delete)
|
||||||
|
, alwaysInvalid(false) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Construct a new Subschema using custom memory management
|
* @brief Construct a new Subschema using custom memory management
|
||||||
@ -53,7 +54,8 @@ public:
|
|||||||
*/
|
*/
|
||||||
Subschema(CustomAlloc allocFn, CustomFree freeFn)
|
Subschema(CustomAlloc allocFn, CustomFree freeFn)
|
||||||
: allocFn(allocFn)
|
: allocFn(allocFn)
|
||||||
, freeFn(freeFn) { }
|
, freeFn(freeFn)
|
||||||
|
, alwaysInvalid(false) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Clean up and free all memory managed by the Subschema
|
* @brief Clean up and free all memory managed by the Subschema
|
||||||
@ -139,6 +141,11 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool getAlwaysInvalid() const
|
||||||
|
{
|
||||||
|
return alwaysInvalid;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the description associated with this sub-schema
|
* @brief Get the description associated with this sub-schema
|
||||||
*
|
*
|
||||||
@ -217,6 +224,11 @@ public:
|
|||||||
return static_cast<bool>(title);
|
return static_cast<bool>(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setAlwaysInvalid(bool value)
|
||||||
|
{
|
||||||
|
alwaysInvalid = value;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set the description for this sub-schema
|
* @brief Set the description for this sub-schema
|
||||||
*
|
*
|
||||||
@ -266,6 +278,8 @@ private:
|
|||||||
// Disable copy assignment
|
// Disable copy assignment
|
||||||
Subschema & operator=(const Subschema &);
|
Subschema & operator=(const Subschema &);
|
||||||
|
|
||||||
|
bool alwaysInvalid;
|
||||||
|
|
||||||
/// List of pointers to constraints that apply to this schema.
|
/// List of pointers to constraints that apply to this schema.
|
||||||
std::vector<const Constraint *> constraints;
|
std::vector<const Constraint *> constraints;
|
||||||
|
|
||||||
|
@ -63,6 +63,10 @@ public:
|
|||||||
*/
|
*/
|
||||||
bool validateSchema(const Subschema &subschema)
|
bool validateSchema(const Subschema &subschema)
|
||||||
{
|
{
|
||||||
|
if (subschema.getAlwaysInvalid()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Wrap the validationCallback() function below so that it will be
|
// Wrap the validationCallback() function below so that it will be
|
||||||
// passed a reference to a constraint (_1), and a reference to the
|
// passed a reference to a constraint (_1), and a reference to the
|
||||||
// visitor (*this).
|
// visitor (*this).
|
||||||
@ -108,10 +112,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual bool visit(const AllOfConstraint &constraint)
|
virtual bool visit(const AllOfConstraint &constraint)
|
||||||
{
|
{
|
||||||
if (constraint.getAlwaysInvalid()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool validated = true;
|
bool validated = true;
|
||||||
constraint.applyToSubschemas(ValidateSubschemas(target, context,
|
constraint.applyToSubschemas(ValidateSubschemas(target, context,
|
||||||
true, false, *this, results, NULL, &validated));
|
true, false, *this, results, NULL, &validated));
|
||||||
@ -138,10 +138,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual bool visit(const AnyOfConstraint &constraint)
|
virtual bool visit(const AnyOfConstraint &constraint)
|
||||||
{
|
{
|
||||||
if (constraint.getAlwaysValid()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int numValidated = 0;
|
unsigned int numValidated = 0;
|
||||||
|
|
||||||
ValidationResults newResults;
|
ValidationResults newResults;
|
||||||
|
@ -446,7 +446,10 @@ TEST_F(TestValidator, Draft7_AnyOf)
|
|||||||
processDraft7TestFile(TEST_SUITE_DIR "draft7/anyOf.json");
|
processDraft7TestFile(TEST_SUITE_DIR "draft7/anyOf.json");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: untested boolean_schema
|
TEST_F(TestValidator, Draft7_BooleanSchema)
|
||||||
|
{
|
||||||
|
processDraft7TestFile(TEST_SUITE_DIR "draft7/boolean_schema.json");
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: untested const
|
// TODO: untested const
|
||||||
|
|
||||||
@ -476,7 +479,10 @@ TEST_F(TestValidator, Draft7_IfThenElse)
|
|||||||
processDraft7TestFile(TEST_SUITE_DIR "draft7/if-then-else.json");
|
processDraft7TestFile(TEST_SUITE_DIR "draft7/if-then-else.json");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: broken items
|
TEST_F(TestValidator, Draft7_Items)
|
||||||
|
{
|
||||||
|
processDraft7TestFile(TEST_SUITE_DIR "draft7/items.json");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(TestValidator, Draft7_Maximum)
|
TEST_F(TestValidator, Draft7_Maximum)
|
||||||
{
|
{
|
||||||
@ -513,18 +519,30 @@ TEST_F(TestValidator, Draft7_MultipleOf)
|
|||||||
processDraft7TestFile(TEST_SUITE_DIR "draft7/multipleOf.json");
|
processDraft7TestFile(TEST_SUITE_DIR "draft7/multipleOf.json");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: broken not
|
TEST_F(TestValidator, Draft7_Not)
|
||||||
|
{
|
||||||
|
processDraft7TestFile(TEST_SUITE_DIR "draft7/not.json");
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: broken oneOf
|
TEST_F(TestValidator, Draft7_OneOf)
|
||||||
|
{
|
||||||
|
processDraft7TestFile(TEST_SUITE_DIR "draft7/oneOf.json");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(TestValidator, Draft7_Pattern)
|
TEST_F(TestValidator, Draft7_Pattern)
|
||||||
{
|
{
|
||||||
processDraft7TestFile(TEST_SUITE_DIR "draft7/pattern.json");
|
processDraft7TestFile(TEST_SUITE_DIR "draft7/pattern.json");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: broken patternProperties
|
TEST_F(TestValidator, Draft7_PatternProperties)
|
||||||
|
{
|
||||||
|
processDraft7TestFile(TEST_SUITE_DIR "draft7/patternProperties.json");
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: broken properties
|
TEST_F(TestValidator, Draft7_Properties)
|
||||||
|
{
|
||||||
|
processDraft7TestFile(TEST_SUITE_DIR "draft7/properties.json");
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: broken ref
|
// TODO: broken ref
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user