SkillAgentSearch skills...

Valijson

Header-only C++ library for JSON Schema validation, with support for many popular parsers

Install / Use

/learn @tristanpenman/Valijson
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Valijson

Valijson is a header-only JSON Schema validation library for Modern C++.

Valijson provides a simple validation API that allows you to load JSON Schemas, and validate documents loaded by one of several supported parser libraries. A compiler with full C++17 support is required.

Project Goals

The goal of this project is to support validation of all constraints available in JSON Schema v7, while being competitive with the performance of a hand-written schema validator.

Version Compatibility

Valijson is currently maintained across three active minor versions. The key difference between each version is the underlying C++ standard that each targets. The legacy v1.0.x series remains compatible with C++14, while the v1.1.x series adopts C++17, and the upcoming v1.2.x line will move to C++20.

| Version | C++ Standard | Notes | Branch |----------|--------------|-------------------------------|-------- | v1.0.x | C++14 | Legacy release line. | v1.0.x | v1.1.x | C++17 | Current C++17-focused series. | master | v1.2.x | C++20 | Planned C++20-ready version. | v1.2.x

The master branch on GitHub currently tracks the v1.1.x series.

Code written for Valijson v1.0.x that already compiles in a C++14 environment should continue to build with minimal adjustments when moving to v1.1 and C++17.

Usage

Clone the repo, including submodules:

git clone --recurse-submodules git@github.com:tristanpenman/valijson.git

The following code snippets show how you might implement a simple validator using RapidJson as the underlying JSON Parser.

Include the necessary headers:

#include <valijson/adapters/rapidjson_adapter.hpp>
#include <valijson/utils/rapidjson_utils.hpp>
#include <valijson/schema.hpp>
#include <valijson/schema_parser.hpp>
#include <valijson/validator.hpp>

These are the classes that we'll be using:

using valijson::Schema;
using valijson::SchemaParser;
using valijson::Validator;
using valijson::adapters::RapidJsonAdapter;

We are going to use RapidJSON to load the schema and the target document:

// Load JSON document using RapidJSON with Valijson helper function
rapidjson::Document mySchemaDoc;
if (!valijson::utils::loadDocument("mySchema.json", mySchemaDoc)) {
    throw std::runtime_error("Failed to load schema document");
}

// Parse JSON schema content using valijson
Schema mySchema;
SchemaParser parser;
RapidJsonAdapter mySchemaAdapter(mySchemaDoc);
parser.populateSchema(mySchemaAdapter, mySchema);

Load a document to validate:

rapidjson::Document myTargetDoc;
if (!valijson::utils::loadDocument("myTarget.json", myTargetDoc)) {
    throw std::runtime_error("Failed to load target document");
}

Validate a document:

Validator validator;
RapidJsonAdapter myTargetAdapter(myTargetDoc);
if (!validator.validate(mySchema, myTargetAdapter, NULL)) {
    throw std::runtime_error("Validation failed.");
}

Note that Valijson's SchemaParser and Validator classes expect you to pass in a RapidJsonAdapter rather than a rapidjson::Document. This is due to the fact that SchemaParser and Validator are template classes that can be used with any of the JSON parsers supported by Valijson.

Exceptions

By default, Valijson classes will not throw exceptions (e.g. when failing to parse a schema). To enable exceptions for these cases, VALIJSON_USE_EXCEPTIONS must be defined. However note that VALIJSON_USE_EXCEPTIONS is defined as interface compile definition of the cmake target, and the definition populates all the targets linking Valijson with cmake.

Strong vs Weak Types

Valijson has a notion of strong and weak typing. By default, strong typing is used. For example, the following will create a validator that uses strong typing:

Validator validator;

This is equivalent to:

Validator validator(Validator::kStrongTypes);

This validator will not attempt to cast between types to satisfy a schema. So the string "23" will not be parsed as a number.

Alternatively, weak typing can be used:

Validator validator(Validator::kWeakTypes);

This will create a validator that will attempt to cast values to satisfy a schema. The original motivation for this was to support the Boost Property Tree library, which can parse JSON, but stores values as strings.

Strict vs Permissive Date/Time Formats

JSON Schema supports validation of certain types using the format keyword. Supported formats include time, date, and date-time. When date-time is used, the input is validated according to RFC 3999. By default, RFC 3999 requires that all date/time strings are unambiguous - i.e. are defined in terms of a local time zone. This is controlled by the Z suffix (for UTC) or a +01:00 style modifier.

Valijson can be configured to allow ambiguous date/time strings.

Validator validator(Validator::kStrongTypes, Validator::kPermissiveDateTime);

The default is strict date/time validation, which is equivalent to:

Validator validator(Validator::kStrongTypes, Validator::kStrictDateTime);

Regular Expression Engine

When enforcing a 'pattern' property, a regular expression engine is used. By default, the default regular expression (DefaultRegexEngine) uses std::regex. Unfortunately, std::regex has no protection against catastrophic backtracking and the implementation in gcc is so suboptimal that it can easily lead to segmentation faults.

This behaviour can be customised by implementing a wrapper for alternative regular expression engine.

To do this, you must implement the following interface:

struct MyRegexpEngine
{
    MyRegexpEngine(const std::string& pattern)
    {
        // implementation specific
    }

    static bool search(const std::string& s, const MyRegexpEngine& r)
    {
        // implementation specific
    }
};

Then to use it, you must define a customer validator type:

    using MyValidator = ValidatorT<MyRegexpEngine>;

Once you've done this, MyValidator can be used in place of the default valijson::Validator type.

Alternatively the library can be instructed to use boost::regex by specifying either valijson_USE_BOOST_REGEX=TRUE in CMake or defining VALIJSON_USE_BOOST_REGEX=1 as a #define before including any valijson headers. Note that the library does not source boost::regex for you when specifying this option- it is assumed you have it already.

Memory Management

Valijson has been designed to safely manage, and eventually free, the memory that is allocated while parsing a schema or validating a document. When working with an externally loaded schema (i.e. one that is populated using the SchemaParser class) you can rely on RAII semantics.

Things get more interesting when you build a schema using custom code, as illustrated in the following snippet. This code demonstrates how you would create a schema to verify that the value of a 'description' property (if present) is always a string:

{
    // Root schema object that manages memory allocated for
    // constraints or sub-schemas
    Schema schema;

    // Allocating memory for a sub-schema returns a const pointer
    // which allows inspection but not mutation. This memory will be
    // freed only when the root schema goes out of scope
    const Subschema *subschema = schema.createSubschema();

    {   // Limited scope, for example purposes

        // Construct a constraint on the stack
        TypeConstraint typeConstraint;
        typeConstraint.addNamedType(TypeConstraint::kString);

        // Constraints are added to a sub-schema via the root schema,
        // which will make a copy of the constraint
        schema.addConstraintToSubschema(typeConstraint, subschema);

        // Constraint on the stack goes out of scope, but the copy
        // held by the root schema continues to exist
    }

    // Include subschema in properties constraint
    PropertiesConstraint propertiesConstraint;
    propertiesConstraint.addPropertySubschema("description", subschema);

    // Add the properties constraint
    schema.addConstraint(propertiesConstraint);

    // Root schema goes out of scope and all allocated memory is freed
}

JSON References

The library includes support for local JSON References. Remote JSON References are supported only when the appropriate callback functions are provided.

Valijson's JSON Reference implementation requires that two callback functions are required. The first is expected to return a pointer to a newly fetched document. Valijson takes ownership of this pointer. The second callback function is used to release ownership of that pointer back to the application. Typically, this would immediately free the memory that was allocated for the document.

Test Suite

Valijson's test suite currently contains several hand-crafted tests and uses the standard JSON Schema Test Suite to test support for parts of the JSON Schema feature set that have been implemented.

cmake

The examples and test suite can be built using cmake:

# Build examples and test suite
mkdir build
cd build
cmake .. -Dvalijson_BUILD_TESTS=ON -Dvalijson_BUILD_EXAMPLES=ON
make

# Run test suite (from build directory)
./test_suite

How to add this library to your cmake target

Valijson can be integrated either as git submodule or with find_package().

Valijson with CMake's FetchContent

If you consume Valijson via FetchContent, you can avoid fetching Git submodules by setting GIT_SUBMODULES to an empty list. Yo

Related Skills

View on GitHub
GitHub Stars409
CategoryCustomer
Updated1d ago
Forks119

Languages

C++

Security Score

100/100

Audited on Apr 2, 2026

No findings