valijson/examples/custom_schema.cpp

223 lines
7.1 KiB
C++

/**
* @file
*
* @brief Demonstrates validation against a manually constructed schema.
*
* This example demonstrates the construction and composition of a Schema object
* using manually created Constraint objects. The following Constraint classes
* are used:
* - EnumConstraint
* - MaxLengthConstraint
* - MinimumConstraint
* - MinLengthConstraint
* - PropertiesConstraint
* - RequiredConstraint
* - TypeConstraint
*
* The MinimumConstraint class provides support for the exclusiveMinimum and
* minimum keywords in JSON Schema. And the PropertiesConstraint class provides
* support for the properties, patternProperties, and additionalProperties
* keywords.
*
* This is the JSON Schema representation of the Schema that will be created:
*
* {
* "properties": {
* "category": {
* "enum": [
* "album",
* "book",
* "other",
* "video"
* ]
* },
* "description": {
* "type": "string"
* },
* "price": {
* "exclusiveMinimum": true,
* "minimum": 0.0,
* "type": "number"
* },
* "title": {
* "maxLength": 200,
* "minLength": 1,
* "type": "string"
* }
* },
* "required": [
* "category",
* "price",
* "title"
* ],
* "type": "object"
* }
*
*/
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <valijson/adapters/rapidjson_adapter.hpp>
#include <valijson/constraints/concrete_constraints.hpp>
#include <valijson/utils/rapidjson_utils.hpp>
#include <valijson/schema.hpp>
#include <valijson/validation_results.hpp>
#include <valijson/validator.hpp>
using std::cerr;
using std::endl;
using valijson::Schema;
using valijson::Subschema;
using valijson::Validator;
using valijson::ValidationResults;
using valijson::adapters::RapidJsonAdapter;
using valijson::adapters::RapidJsonFrozenValue;
using valijson::constraints::EnumConstraint;
using valijson::constraints::MaxLengthConstraint;
using valijson::constraints::MinimumConstraint;
using valijson::constraints::MinLengthConstraint;
using valijson::constraints::PropertiesConstraint;
using valijson::constraints::RequiredConstraint;
using valijson::constraints::TypeConstraint;
void addPropertiesConstraint(Schema &schema)
{
PropertiesConstraint propertiesConstraint;
{
// Prepare an enum constraint requires a document to be equal to at
// least one of a set of possible values
EnumConstraint constraint;
constraint.addValue(RapidJsonFrozenValue("album"));
constraint.addValue(RapidJsonFrozenValue("book"));
constraint.addValue(RapidJsonFrozenValue("other"));
constraint.addValue(RapidJsonFrozenValue("video"));
// Create a subschema, owned by the root schema, with a constraint
const Subschema *subschema = schema.createSubschema();
schema.addConstraintToSubschema(constraint, subschema);
// Include subschema in properties constraint
propertiesConstraint.addPropertySubschema("category", subschema);
}
{
// Create a child schema for the 'description' property that requires
// a string, but does not enforce any length constraints.
const Subschema *subschema = schema.createSubschema();
TypeConstraint typeConstraint;
typeConstraint.addNamedType(TypeConstraint::kString);
schema.addConstraintToSubschema(typeConstraint, subschema);
// Include subschema in properties constraint
propertiesConstraint.addPropertySubschema("description", subschema);
}
{
// Create a child schema for the 'price' property, that requires a
// number with a value greater than zero.
const Subschema *subschema = schema.createSubschema();
MinimumConstraint minimumConstraint;
minimumConstraint.setMinimum(0.0);
minimumConstraint.setExclusiveMinimum(true);
schema.addConstraintToSubschema(minimumConstraint, subschema);
TypeConstraint typeConstraint;
typeConstraint.addNamedType(TypeConstraint::kNumber);
schema.addConstraintToSubschema(typeConstraint, subschema);
// Include subschema in properties constraint
propertiesConstraint.addPropertySubschema("price", subschema);
}
{
// Create a child schema for the 'title' property that requires a string
// that is between 1 and 200 characters in length.
const Subschema *subschema = schema.createSubschema();
MaxLengthConstraint maxLengthConstraint;
maxLengthConstraint.setMaxLength(200);
schema.addConstraintToSubschema(maxLengthConstraint, subschema);
MinLengthConstraint minLengthConstraint;
minLengthConstraint.setMinLength(0);
schema.addConstraintToSubschema(minLengthConstraint, subschema);
TypeConstraint typeConstraint;
typeConstraint.addNamedType(TypeConstraint::kString);
schema.addConstraintToSubschema(typeConstraint, subschema);
// Include subschema in properties constraint
propertiesConstraint.addPropertySubschema("title", subschema);
}
// Add a PropertiesConstraint to the root schema
schema.addConstraint(propertiesConstraint);
}
void addRequiredConstraint(Schema &schema)
{
// Add a RequiredConstraint to the schema, specifying that the category,
// price, and title properties must be present.
RequiredConstraint constraint;
constraint.addRequiredProperty("category");
constraint.addRequiredProperty("price");
constraint.addRequiredProperty("title");
schema.addConstraint(constraint);
}
void addTypeConstraint(Schema &schema)
{
// Add a TypeConstraint to the schema, specifying that the root of the
// document must be an object.
TypeConstraint typeConstraint;
typeConstraint.addNamedType(TypeConstraint::kObject);
schema.addConstraint(typeConstraint);
}
int main(int argc, char *argv[])
{
if (argc != 2) {
cerr << "Usage:" << endl;
cerr << " ./custom_schema <document>" << endl;
cerr << endl;
return 1;
}
// Load the document that is to be validated
rapidjson::Document targetDocument;
if (!valijson::utils::loadDocument(argv[1], targetDocument)) {
cerr << "Failed to load target document." << endl;
return 1;
}
// Populate a schema
Schema schema;
addPropertiesConstraint(schema);
addRequiredConstraint(schema);
addTypeConstraint(schema);
// Perform validation
Validator validator;
ValidationResults results;
RapidJsonAdapter targetDocumentAdapter(targetDocument);
if (!validator.validate(schema, targetDocumentAdapter, &results)) {
std::cerr << "Validation failed." << endl;
ValidationResults::Error error;
unsigned int errorNum = 1;
while (results.popError(error)) {
cerr << "Error #" << errorNum << std::endl;
cerr << " ";
for (const std::string &contextElement : error.context) {
cerr << contextElement << " ";
}
cerr << endl;
cerr << " - " << error.description << endl;
++errorNum;
}
return 1;
}
return 0;
}