Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/pboettch/json-schema-validator
JSON schema validator for JSON for Modern C++
https://github.com/pboettch/json-schema-validator
Last synced: 2 months ago
JSON representation
JSON schema validator for JSON for Modern C++
- Host: GitHub
- URL: https://github.com/pboettch/json-schema-validator
- Owner: pboettch
- License: other
- Created: 2016-12-22T17:02:22.000Z (almost 8 years ago)
- Default Branch: main
- Last Pushed: 2024-05-13T13:45:57.000Z (5 months ago)
- Last Synced: 2024-07-12T04:37:20.926Z (3 months ago)
- Language: C++
- Homepage:
- Size: 401 KB
- Stars: 476
- Watchers: 20
- Forks: 135
- Open Issues: 29
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
[![Build Status](https://travis-ci.org/pboettch/json-schema-validator.svg?branch=master)](https://travis-ci.org/pboettch/json-schema-validator)
# JSON schema validator for JSON for Modern C++
# What is it?
This is a C++ library for validating JSON documents based on a
[JSON Schema](http://json-schema.org/) which itself should validate with
[draft-7 of JSON Schema Validation](http://json-schema.org/schema).First a disclaimer: *It is work in progress and
contributions or hints or discussions are welcome.*Niels Lohmann et al develop a great JSON parser for C++ called [JSON for Modern
C++](https://github.com/nlohmann/json). This validator is based on this
library, hence the name.External documentation is missing as well. However the API of the validator
is rather simple.# New in version 2
Although significant changes have been done for the 2nd version
(a complete rewrite) the API is compatible with the 1.0.0 release. Except for
the namespace which is now `nlohmann::json_schema`.Version **2** supports JSON schema draft 7, whereas 1 was supporting draft 4
only. Please update your schemas.The primary change in 2 is the way a schema is used. While in version 1 the schema was
kept as a JSON-document and used again and again during validation, in version 2 the schema
is parsed into compiled C++ objects which are then used during validation. There are surely
still optimizations to be done, but validation speed has improved by factor 100
or more.# Design goals
The main goal of this validator is to produce *human-comprehensible* error
messages if a JSON-document/instance does not comply to its schema.By default this is done with exceptions thrown at the users with a helpful
message telling what's wrong with the document while validating.Starting with **2.0.0** the user can pass a `json_schema::basic_error_handler`-derived
object along with the instance to validate to receive a callback each time
a validation error occurs and decide what to do (throwing, counting, collecting).Another goal was to use Niels Lohmann's JSON-library. This is why the validator
lives in his namespace.# Thread-safety
Instance validation is thread-safe and the same validator-object can be used by
different threads:The validate method is `const` which indicates the object is not modified when
being called:```C++
json json_validator::validate(const json &) const;
```Validator-object creation however is not thread-safe. A validator has to be
created in one (main?) thread once.# Weaknesses
Numerical validation uses nlohmann-json's integer, unsigned and floating point
types, depending on if the schema type is "integer" or "number". Bignum
(i.e. arbitrary precision and range) is not supported at this time.# Building
This library is based on Niels Lohmann's JSON-library and thus has
a build-dependency to it.Currently at least version **3.8.0** of NLohmann's JSON library
is required.Various methods using CMake can be used to build this project.
## Build out-of-source
Do not run cmake inside the source-dir. Rather create a dedicated build-dir:
```Bash
git clone https://github.com/pboettch/json-schema-validator.git
cd json-schema-validator
mkdir build
cd build
cmake [..]
make
make install # if needed
ctest # run unit, non-regression and test-suite tests
```## Building as shared library
By default a static library is built. Shared libraries can be generated by using
the `BUILD_SHARED_LIBS`-cmake variable:In your initial call to cmake simply add:
```bash
cmake [..] -DBUILD_SHARED_LIBS=ON [..]
```## nlohmann-json integration
As nlohmann-json is a dependency, this library tries find it.
The cmake-configuration first checks if nlohmann-json is available as a cmake-target. This may be the case, because it is used as a submodule in a super-project which already provides and uses nlohmann-json.
Otherwise, it calls `find_package` for nlohmann-json and requires nlohmann-json to be installed on the system.### Building with Hunter package manager
To enable access to nlohmann json library, Hunter can be used. Just run with `JSON_VALIDATOR_HUNTER=ON` option. No further dependencies needed
```bash
cmake [..] -DJSON_VALIDATOR_HUNTER=ON [..]
```### Building as a CMake-subdirectory from within another project
Adding this library as a subdirectory to a parent project is one way of
building it.If the parent project already used `find_package()` to find the CMake-package of nlohmann_json or includes it as a submodule likewise.
### Building directly, finding a CMake-package. (short)
When nlohmann-json has been installed, it provides files which allows
CMake's `find_package()` to be used.This library is using this mechanism if `nlohmann_json::nlohmann_json`-target
does not exist.### Install
Since version 2.1.0 this library can be installed and CMake-package-files will be
created accordingly. If the installation of nlohmann-json and this library
is done into default unix-system-paths CMake will be able to find this
library by simply doing:```CMake
find_package(nlohmann_json_schema_validator REQUIRED)
```and
```CMake
target_link_libraries( [..] nlohmann_json_schema_validator)
```to build and link.
## Code
See also `app/json-schema-validate.cpp`.
```C++
#include
#include#include
using nlohmann::json;
using nlohmann::json_schema::json_validator;// The schema is defined based upon a string literal
static json person_schema = R"(
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "A person",
"properties": {
"name": {
"description": "Name",
"type": "string"
},
"age": {
"description": "Age of the person",
"type": "number",
"minimum": 2,
"maximum": 200
}
},
"required": [
"name",
"age"
],
"type": "object"
})"_json;
// The people are defined with brace initialization
static json bad_person = {{"age", 42}};
static json good_person = {{"name", "Albert"}, {"age", 42}};int main()
{
/* json-parse the schema */json_validator validator; // create validator
try {
validator.set_root_schema(person_schema); // insert root-schema
} catch (const std::exception &e) {
std::cerr << "Validation of schema failed, here is why: " << e.what() << "\n";
return EXIT_FAILURE;
}/* json-parse the people - API of 1.0.0, default throwing error handler */
for (auto &person : {bad_person, good_person}) {
std::cout << "About to validate this person:\n"
<< std::setw(2) << person << std::endl;
try {
validator.validate(person); // validate the document - uses the default throwing error-handler
std::cout << "Validation succeeded\n";
} catch (const std::exception &e) {
std::cerr << "Validation failed, here is why: " << e.what() << "\n";
}
}/* json-parse the people - with custom error handler */
class custom_error_handler : public nlohmann::json_schema::basic_error_handler
{
void error(const nlohmann::json_pointer> &pointer, const json &instance,
const std::string &message) override
{
nlohmann::json_schema::basic_error_handler::error(pointer, instance, message);
std::cerr << "ERROR: '" << pointer << "' - '" << instance << "': " << message << "\n";
}
};for (auto &person : {bad_person, good_person}) {
std::cout << "About to validate this person:\n"
<< std::setw(2) << person << std::endl;custom_error_handler err;
validator.validate(person, err); // validate the documentif (err)
std::cerr << "Validation failed\n";
else
std::cout << "Validation succeeded\n";
}return EXIT_SUCCESS;
}
```# Compliance
There is an application which can be used for testing the validator with the
[JSON-Schema-Test-Suite](https://github.com/json-schema-org/JSON-Schema-Test-Suite).
In order to simplify the testing, the test-suite is included in the repository.If you have cloned this repository providing a path the repository-root via the
cmake-variable `JSON_SCHEMA_TEST_SUITE_PATH` will enable the test-target(s).All required tests are **OK**.
# Format
Optionally JSON-schema-validator can validate predefined or user-defined formats.
Therefore a format-checker-function can be provided by the user which is called by
the validator when a format-check is required (ie. the schema contains a format-field).This is how the prototype looks like and how it can be passed to the validation-instance:
```C++
static void my_format_checker(const std::string &format, const std::string &value)
{
if (format == "something") {
if (!check_value_for_something(value))
throw std::invalid_argument("value is not a good something");
} else
throw std::logic_error("Don't know how to validate " + format);
}// when creating the validator
json_validator validator(nullptr, // or loader-callback
my_format_checker); // create validator
```## Default Checker
The library contains a default-checker, which does some checks. It needs to be
provided manually to the constructor of the validator:```C++
json_validator validator(loader, // or nullptr for no loader
nlohmann::json_schema::default_string_format_check);
```Supported formats: `date-time, date, time, email, hostname, ipv4, ipv6, uuid, regex`
More formats can be added in `src/string-format-check.cpp`. Please contribute implementions for missing json schema draft formats.
## Default value processing
As a result of the validation, the library returns a json patch including the default values of the specified schema.
```C++
#include
#includeusing nlohmann::json;
using nlohmann::json_schema::json_validator;static const json rectangle_schema = R"(
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "A rectangle",
"properties": {
"width": {
"$ref": "#/definitions/length",
"default": 20
},
"height": {
"$ref": "#/definitions/length"
}
},
"definitions": {
"length": {
"type": "integer",
"minimum": 1,
"default": 10
}
}
})"_json;int main()
{
try {
json_validator validator{rectangle_schema};
/* validate empty json -> will be expanded by the default values defined in the schema */
json rectangle = "{}"_json;
const auto default_patch = validator.validate(rectangle);
rectangle = rectangle.patch(default_patch);
std::cout << rectangle.dump() << std::endl; // {"height":10,"width":20}
} catch (const std::exception &e) {
std::cerr << "Validation of schema failed: " << e.what() << "\n";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
```The example above will output the specified default values `{"height":10,"width":20}` to stdout.
> Note that the default value specified in a `$ref` may be overridden by the current instance location. Also note that this behavior will break draft-7, but it is compliant to newer drafts (e.g. `2019-09` or `2020-12`).
# Contributing
This project uses [`pre-commit`](https://pre-commit.com/) to enforce style-checks. Please install and run it before
creating commits and making pull requests.```console
$ pip install pre-commit
$ pre-commit install
```