Schema a now "parsed" into C++-validator-objects in a first step and then validation takes place with these objects. Errors are now handled via a user-provided error-handler allowing the user to collect all errors at once or bail out when a certain threshold is reached. Fixes #36 and #8. One (sub-)schema can now be referenced with different URIs. Fixes #9 JSON schema draft 7 is now supported. Fixes #35
224 lines
6.8 KiB
Markdown
224 lines
6.8 KiB
Markdown
|
|
[](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.* Even though a 2.0.0 release is immenent.
|
|
|
|
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 coorporate to the 2 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 versin 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.
|
|
|
|
In JSON-schema one sub-schema can be
|
|
|
|
# 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.
|
|
|
|
With **2.0.0** the user can passed a `json_scheam::basic_error_handler` derived object
|
|
along with the instance to validate to receive a each time a validation error occurs
|
|
and decice 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.
|
|
|
|
# Weaknesses
|
|
|
|
Numerical validation uses nlohmann 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.
|
|
|
|
Currently JSON-URI with "plain name fragments" are not supported. So referring to an URI
|
|
with `$ref: "file.json#plain"` will not work.
|
|
|
|
# How to use
|
|
|
|
The current state of the build-system needs at least version **3.5.0** of NLohmann's
|
|
JSON library. It is looking for the `json.hpp` within a `nlohmann/`-path.
|
|
|
|
When build the library you need to provide the path to the directory where the include-file
|
|
is located as `nlohmann/json.hpp`.
|
|
|
|
## Build
|
|
|
|
### Within a build-dir
|
|
|
|
```Bash
|
|
git clone https://github.com/pboettch/json-schema-validator.git
|
|
cd json-schema-validator
|
|
mkdir build
|
|
cd build
|
|
cmake .. \
|
|
-DNLOHMANN_JSON_DIR=<path/to/>nlohmann/json.hpp \
|
|
-DJSON_SCHEMA_TEST_SUITE_PATH=<path/to/JSON-Schema-test-suite> # optional
|
|
make # install
|
|
ctest # run unit, non-regression and test-suite tests
|
|
```
|
|
### As a subdirectory from within
|
|
|
|
```CMake
|
|
# create an interface-target called json-hpp
|
|
add_library(json-hpp INTERFACE)
|
|
target_include_directories(json-hpp
|
|
INTERFACE
|
|
path/to/nlohmann/json.hpp)
|
|
|
|
# set this path to schema-test-suite to get tests compiled - optional
|
|
set(JSON_SCHEMA_TEST_SUITE_PATH "path/to/json-schema-test-suite")
|
|
enable_testing() # if you want to inherit tests
|
|
|
|
add_subdirectory(path-to-this-project json-schema-validator)
|
|
```
|
|
|
|
### Building a shared library
|
|
|
|
By default a static library is built. Shared libraries are generated by using
|
|
the `BUILD_SHARED_LIBS`-cmake variable:
|
|
|
|
In your initial call to cmake simply add:
|
|
```bash
|
|
cmake -DBUILD_SHARED_LIBS=ON
|
|
```
|
|
## Code
|
|
|
|
See also `app/json-schema-validate.cpp`.
|
|
|
|
```C++
|
|
#include <iostream>
|
|
|
|
#include "json-schema.hpp"
|
|
|
|
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
|
|
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 std::string &path, const json &instance, const std::string &message) override
|
|
{
|
|
nlohmann::json_schema::basic_error_handler::error(path, instance, message);
|
|
std::cerr << "ERROR: '" << path << "' - '" << 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 document - uses the default throwing error-handler
|
|
|
|
if (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**.
|
|
|
|
# Additional features
|
|
|
|
## Default values
|
|
|
|
The goal is to create an empty document, based on schema-defined
|
|
default-values, recursively populated.
|