Initial support for 'if', 'then' and 'else' subschemas

This commit is contained in:
Tristan Penman 2019-08-29 10:31:37 +10:00
parent dac4cff42f
commit 925ff8ff68
4 changed files with 115 additions and 4 deletions

View File

@ -118,7 +118,51 @@ private:
class ConditionalConstraint: public BasicConstraint<ConditionalConstraint> class ConditionalConstraint: public BasicConstraint<ConditionalConstraint>
{ {
public: public:
ConditionalConstraint() { } ConditionalConstraint()
: ifSubschema(NULL),
thenSubschema(NULL),
elseSubschema(NULL) { }
ConditionalConstraint(CustomAlloc allocFn, CustomFree freeFn)
: BasicConstraint(allocFn, freeFn),
ifSubschema(NULL),
thenSubschema(NULL),
elseSubschema(NULL) { }
const Subschema * getIfSubschema() const
{
return ifSubschema;
}
const Subschema * getThenSubschema() const
{
return thenSubschema;
}
const Subschema * getElseSubschema() const
{
return elseSubschema;
}
void setIfSubschema(const Subschema *subschema)
{
ifSubschema = subschema;
}
void setThenSubschema(const Subschema *subschema)
{
thenSubschema = subschema;
}
void setElseSubschema(const Subschema *subschema)
{
elseSubschema = subschema;
}
private:
const Subschema *ifSubschema;
const Subschema *thenSubschema;
const Subschema *elseSubschema;
}; };
/** /**

View File

@ -37,7 +37,8 @@ public:
/// Supported versions of JSON Schema /// Supported versions of JSON Schema
enum Version { enum Version {
kDraft3, ///< @deprecated JSON Schema v3 has been superseded by v4 kDraft3, ///< @deprecated JSON Schema v3 has been superseded by v4
kDraft4 kDraft4,
kDraft7
}; };
/// Version of JSON Schema that should be expected when parsing /// Version of JSON Schema that should be expected when parsing
@ -710,6 +711,25 @@ private:
} }
} }
{
const typename AdapterType::Object::const_iterator ifItr = object.find("if");
const typename AdapterType::Object::const_iterator thenItr = object.find("then");
const typename AdapterType::Object::const_iterator elseItr = object.find("end");
if (object.end() != ifItr && object.end() != thenItr) {
if (version == kDraft7) {
rootSchema.addConstraintToSubschema(
makeConditionalConstraint(rootSchema, rootNode,
ifItr->second, thenItr->second,
elseItr == object.end() ? NULL : &elseItr->second,
updatedScope, nodePath, fetchDoc, docCache, schemaCache),
&subschema);
} else {
throw std::runtime_error("Not supported");
}
}
}
if ((itr = object.find("maximum")) != object.end()) { if ((itr = object.find("maximum")) != object.end()) {
typename AdapterType::Object::const_iterator exclusiveMaximumItr = typename AdapterType::Object::const_iterator exclusiveMaximumItr =
object.find("exclusiveMaximum"); object.find("exclusiveMaximum");
@ -1138,6 +1158,24 @@ private:
{ {
constraints::ConditionalConstraint constraint; constraints::ConditionalConstraint constraint;
const Subschema *ifSubschema = makeOrReuseSchema<AdapterType>(
rootSchema, rootNode, ifNode, currentScope,
nodePath, fetchDoc, NULL, NULL, docCache,
schemaCache);
constraint.setIfSubschema(ifSubschema);
const Subschema *thenSubschema = makeOrReuseSchema<AdapterType>(
rootSchema, rootNode, thenNode, currentScope, nodePath, fetchDoc, NULL, NULL,
docCache, schemaCache);
constraint.setThenSubschema(thenSubschema);
if (elseNode) {
const Subschema *elseSubschema = makeOrReuseSchema<AdapterType>(
rootSchema, rootNode, *elseNode, currentScope, nodePath, fetchDoc, NULL, NULL,
docCache, schemaCache);
constraint.setElseSubschema(elseSubschema);
}
return constraint; return constraint;
} }

View File

@ -158,11 +158,30 @@ public:
return numValidated > 0; return numValidated > 0;
} }
/**
* @brief Validate current node using a set of 'if', 'then' and 'else' subschemas
*
* A conditional constraint allows a document to validated against one of two additional
* subschemas (specified via 'then' or 'else' properties) depending on whether the document
* satifies an optional subschema (specified via the 'if' property).
*
* @param constraint ConditionalConstraint that the current node must validate against
*
* @return \c true if validation passes; \c false otherwise
*/
virtual bool visit(const ConditionalConstraint &constraint) virtual bool visit(const ConditionalConstraint &constraint)
{ {
return false; // Create a validator to evaluate the conditional
ValidationVisitor ifValidator(target, context, strictTypes, NULL);
ValidationVisitor thenElseValidator(target, context, strictTypes, NULL);
if (ifValidator.validateSchema(*constraint.getIfSubschema())) {
return thenElseValidator.validateSchema(*constraint.getThenSubschema());
} else {
return thenElseValidator.validateSchema(*constraint.getElseSubschema());
}
} }
/** /**
* @brief Validate current node against a 'dependencies' constraint * @brief Validate current node against a 'dependencies' constraint
* *

View File

@ -183,6 +183,11 @@ protected:
{ {
return processTestFile(testFile, SchemaParser::kDraft4); return processTestFile(testFile, SchemaParser::kDraft4);
} }
void processDraft7TestFile(const std::string &testFile)
{
return processTestFile(testFile, SchemaParser::kDraft7);
}
}; };
TEST_F(TestValidator, Draft3_AdditionalItems) TEST_F(TestValidator, Draft3_AdditionalItems)
@ -414,3 +419,8 @@ TEST_F(TestValidator, Draft4_UniqueItems)
{ {
processDraft4TestFile(TEST_SUITE_DIR "draft4/uniqueItems.json"); processDraft4TestFile(TEST_SUITE_DIR "draft4/uniqueItems.json");
} }
TEST_F(TestValidator, Draft7_IfThenElse)
{
processDraft7TestFile(TEST_SUITE_DIR "draft7/if-then-else.json");
}