Update nlohmann::json to 3.11.2

Also update json_schema_validator to match new deprecations
This commit is contained in:
Jon Evans 2022-11-03 22:22:35 -04:00
parent 9fafd6feff
commit 995a153f27
15 changed files with 6976 additions and 7464 deletions

View File

@ -33,6 +33,8 @@
#include <boost/uuid/entropy_error.hpp>
#endif
#include <nlohmann/json.hpp>
#include <cctype>
#include <mutex>
@ -336,3 +338,15 @@ wxString KIID_PATH::AsString() const
return path;
}
void to_json( nlohmann::json& aJson, const KIID& aKIID )
{
aJson = aKIID.AsString().ToUTF8();
}
void from_json( const nlohmann::json& aJson, KIID& aKIID )
{
aKIID = KIID( aJson.get<std::string>() );
}

View File

@ -120,12 +120,6 @@ bool JSON_SETTINGS::Contains( const std::string& aPath ) const
}
size_t JSON_SETTINGS::Count( const std::string& aPath ) const
{
return m_internals->count( JSON_SETTINGS_INTERNALS::PointerFromString( aPath ) );
}
JSON_SETTINGS_INTERNALS* JSON_SETTINGS::Internals()
{
return m_internals.get();

View File

@ -77,6 +77,9 @@ principle should be easily implemented by adapting the current STL containers.
%ignore operator <<;
%ignore operator=;
%ignore to_json;
%ignore from_json;
// headers/imports that must be included in the _wrapper.cpp at top
%{

View File

@ -28,6 +28,7 @@
#include <boost/uuid/uuid.hpp>
#include <macros_swig.h>
#include <nlohmann/json_fwd.hpp>
#include <string>
@ -197,4 +198,8 @@ public:
}
};
void to_json( nlohmann::json& aJson, const KIID& aKIID );
void from_json( const nlohmann::json& aJson, KIID& aKIID );
#endif // KIID_H

View File

@ -91,7 +91,6 @@ public:
nlohmann::json& At( const std::string& aPath );
bool Contains( const std::string& aPath ) const;
size_t Count( const std::string& aPath ) const;
JSON_SETTINGS_INTERNALS* Internals();

View File

@ -963,31 +963,40 @@ bool BOARD_DESIGN_SETTINGS::LoadFromFile( const wxString& aDirectory )
return std::string( name.ToUTF8() );
};
std::string bp = "board.design_settings.rule_severities.";
std::string rs = "rule_severities.";
const std::string rs = "rule_severities.";
const std::string no_courtyard_key = "legacy_no_courtyard_defined";
const std::string courtyard_overlap_key = "legacy_courtyards_overlap";
if( std::optional<bool> v = project->Get<bool>( bp + "legacy_no_courtyard_defined" ) )
try
{
if( *v )
Set( rs + drcName( DRCE_MISSING_COURTYARD ), "error" );
else
Set( rs + drcName( DRCE_MISSING_COURTYARD ), "ignore" );
nlohmann::json& severities =
project->Internals()->at( "/board/design_settings/rule_severities"_json_pointer );
project->Internals()->erase( m_internals->PointerFromString( bp + "legacy_no_courtyard_defined" ) );
migrated = true;
if( severities.contains( no_courtyard_key ) )
{
if( severities[no_courtyard_key].get<bool>() )
Set( rs + drcName( DRCE_MISSING_COURTYARD ), "error" );
else
Set( rs + drcName( DRCE_MISSING_COURTYARD ), "ignore" );
severities.erase( no_courtyard_key );
migrated = true;
}
if( severities.contains( courtyard_overlap_key ) )
{
if( severities[courtyard_overlap_key].get<bool>() )
Set( rs + drcName( DRCE_OVERLAPPING_FOOTPRINTS ), "error" );
else
Set( rs + drcName( DRCE_OVERLAPPING_FOOTPRINTS ), "ignore" );
severities.erase( courtyard_overlap_key );
migrated = true;
}
}
if( std::optional<bool> v = project->Get<bool>( bp + "legacy_courtyards_overlap" ) )
catch( ... )
{
if( *v )
Set( rs + drcName( DRCE_OVERLAPPING_FOOTPRINTS ), "error" );
else
Set( rs + drcName( DRCE_OVERLAPPING_FOOTPRINTS ), "ignore" );
project->Internals()->erase( JSON_SETTINGS_INTERNALS::PointerFromString( bp + "legacy_courtyards_overlap" ) );
migrated = true;
}
if( Contains( "legacy" ) )
{
// This defaults to false for new boards, but version 5.1.x and prior kept the fillets

View File

@ -435,7 +435,7 @@ bool FOOTPRINT_EDITOR_SETTINGS::migrateSchema0to1()
std::string theme_ptr( "appearance.color_theme" );
if( !Count( theme_ptr ) )
if( !Contains( theme_ptr ) )
return true;
wxString selected = At( theme_ptr ).get<wxString>();

View File

@ -85,19 +85,19 @@ json_patch::json_patch(const json &patch)
json_patch &json_patch::add(const json::json_pointer &ptr, json value)
{
j_.push_back(json{{"op", "add"}, {"path", ptr}, {"value", std::move(value)}});
j_.push_back(json{{"op", "add"}, {"path", ptr.to_string()}, {"value", std::move(value)}});
return *this;
}
json_patch &json_patch::replace(const json::json_pointer &ptr, json value)
{
j_.push_back(json{{"op", "replace"}, {"path", ptr}, {"value", std::move(value)}});
j_.push_back(json{{"op", "replace"}, {"path", ptr.to_string()}, {"value", std::move(value)}});
return *this;
}
json_patch &json_patch::remove(const json::json_pointer &ptr)
{
j_.push_back(json{{"op", "remove"}, {"path", ptr}});
j_.push_back(json{{"op", "remove"}, {"path", ptr.to_string()}});
return *this;
}

View File

@ -14,6 +14,7 @@
#include <memory>
#include <set>
#include <sstream>
#include <string>
using nlohmann::json;
using nlohmann::json_patch;
@ -34,12 +35,21 @@ using namespace nlohmann::json_schema;
namespace
{
static const json EmptyDefault = nullptr;
class schema
{
protected:
root_schema *root_;
json default_value_ = nullptr;
protected:
virtual std::shared_ptr<schema> make_for_default_(
std::shared_ptr<::schema> & /* sch */,
root_schema * /* root */,
std::vector<nlohmann::json_uri> & /* uris */,
nlohmann::json & /* default_value */) const
{
return nullptr;
};
public:
virtual ~schema() = default;
@ -49,11 +59,13 @@ public:
virtual void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const = 0;
virtual const json &defaultValue(const json::json_pointer &, const json &, error_handler &) const
virtual const json &default_value(const json::json_pointer &, const json &, error_handler &) const
{
return EmptyDefault;
return default_value_;
}
void set_default_value(const json &v) { default_value_ = v; }
static std::shared_ptr<schema> make(json &schema,
root_schema *root,
const std::vector<std::string> &key,
@ -64,6 +76,8 @@ class schema_ref : public schema
{
const std::string id_;
std::weak_ptr<schema> target_;
std::shared_ptr<schema> target_strong_; // for references to references keep also the shared_ptr because
// no one else might use it after resolving
void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const final
{
@ -75,24 +89,47 @@ class schema_ref : public schema
e.error(ptr, instance, "unresolved or freed schema-reference " + id_);
}
const json &defaultValue(const json::json_pointer &ptr, const json &instance, error_handler &e) const override
const json &default_value(const json::json_pointer &ptr, const json &instance, error_handler &e) const override final
{
if (!default_value_.is_null())
return default_value_;
auto target = target_.lock();
if (target)
return target->defaultValue(ptr, instance, e);
else
e.error(ptr, instance, "unresolved or freed schema-reference " + id_);
return target->default_value(ptr, instance, e);
return EmptyDefault;
e.error(ptr, instance, "unresolved or freed schema-reference " + id_);
return default_value_;
}
protected:
virtual std::shared_ptr<schema> make_for_default_(
std::shared_ptr<::schema> &sch,
root_schema *root,
std::vector<nlohmann::json_uri> &uris,
nlohmann::json &default_value) const override
{
// create a new reference schema using the original reference (which will be resolved later)
// to store this overloaded default value #209
auto result = std::make_shared<schema_ref>(uris[0].to_string(), root);
result->set_target(sch, true);
result->set_default_value(default_value);
return result;
};
public:
schema_ref(const std::string &id, root_schema *root)
: schema(root), id_(id) {}
const std::string &id() const { return id_; }
void set_target(const std::shared_ptr<schema> &target) { target_ = target; }
void set_target(const std::shared_ptr<schema> &target, bool strong = false)
{
target_ = target;
if (strong)
target_strong_ = target;
}
};
} // namespace
@ -168,7 +205,7 @@ public:
auto fragment = new_uri.pointer();
// is there a reference looking for this unknown-keyword, which is thus no longer a unknown keyword but a schema
auto unresolved = file.unresolved.find(fragment);
auto unresolved = file.unresolved.find(fragment.to_string());
if (unresolved != file.unresolved.end())
schema::make(value, this, {}, {{new_uri}});
else { // no, nothing ref'd it, keep for later
@ -216,7 +253,7 @@ public:
//
// an unknown keyword can only be referenced by a json-pointer,
// not by a plain name fragment
if (uri.pointer() != "") {
if (uri.pointer().to_string() != "") {
try {
auto &subschema = file.unknown_keywords.at(uri.pointer()); // null is returned if not existing
auto s = schema::make(subschema, this, {}, {{uri}}); // A JSON Schema MUST be an object or a boolean.
@ -272,11 +309,31 @@ public:
break;
} while (1);
for (const auto &file : files_)
if (file.second.unresolved.size() != 0)
for (const auto &file : files_) {
if (file.second.unresolved.size() != 0) {
// Build a representation of the undefined
// references as a list of comma-separated strings.
auto n_urefs = file.second.unresolved.size();
std::string urefs = "[";
decltype(n_urefs) counter = 0;
for (const auto &p : file.second.unresolved) {
urefs += p.first;
if (counter != n_urefs - 1u) {
urefs += ", ";
}
++counter;
}
urefs += "]";
throw std::invalid_argument("after all files have been parsed, '" +
(file.first == "" ? "<root>" : file.first) +
"' has still undefined references.");
"' has still the following undefined references: " + urefs);
}
}
}
void validate(const json::json_pointer &ptr,
@ -347,9 +404,9 @@ class logical_not : public schema
e.error(ptr, instance, "the subschema has succeeded, but it is required to not validate");
}
const json &defaultValue(const json::json_pointer &ptr, const json &instance, error_handler &e) const override
const json &default_value(const json::json_pointer &ptr, const json &instance, error_handler &e) const override
{
return subschema_->defaultValue(ptr, instance, e);
return subschema_->default_value(ptr, instance, e);
}
public:
@ -443,7 +500,6 @@ bool logical_combination<oneOf>::is_validate_complete(const json &instance, cons
class type_schema : public schema
{
json defaultValue_ = EmptyDefault;
std::vector<std::shared_ptr<schema>> type_;
std::pair<bool, json> enum_, const_;
std::vector<std::shared_ptr<schema>> logic_;
@ -456,15 +512,10 @@ class type_schema : public schema
std::shared_ptr<schema> if_, then_, else_;
const json &defaultValue(const json::json_pointer &, const json &, error_handler &) const override
{
return defaultValue_;
}
void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const override final
{
// depending on the type of instance run the type specific validator - if present
auto type = type_[(uint8_t) instance.type()];
auto type = type_[static_cast<uint8_t>(instance.type())];
if (type)
type->validate(ptr, instance, patch, e);
@ -504,11 +555,23 @@ class type_schema : public schema
}
}
protected:
virtual std::shared_ptr<schema> make_for_default_(
std::shared_ptr<::schema> & /* sch */,
root_schema * /* root */,
std::vector<nlohmann::json_uri> & /* uris */,
nlohmann::json &default_value) const override
{
auto result = std::make_shared<type_schema>(*this);
result->set_default_value(default_value);
return result;
};
public:
type_schema(json &sch,
root_schema *root,
const std::vector<nlohmann::json_uri> &uris)
: schema(root), type_((uint8_t) json::value_t::discarded + 1)
: schema(root), type_(static_cast<uint8_t>(json::value_t::discarded) + 1)
{
// association between JSON-schema-type and NLohmann-types
static const std::vector<std::pair<std::string, json::value_t>> schema_types = {
@ -526,7 +589,7 @@ public:
auto attr = sch.find("type");
if (attr == sch.end()) // no type field means all sub-types possible
for (auto &t : schema_types)
type_[(uint8_t) t.second] = type_schema::make(sch, t.second, root, uris, known_keywords);
type_[static_cast<uint8_t>(t.second)] = type_schema::make(sch, t.second, root, uris, known_keywords);
else {
switch (attr.value().type()) { // "type": "type"
@ -534,14 +597,14 @@ public:
auto schema_type = attr.value().get<std::string>();
for (auto &t : schema_types)
if (t.first == schema_type)
type_[(uint8_t) t.second] = type_schema::make(sch, t.second, root, uris, known_keywords);
type_[static_cast<uint8_t>(t.second)] = type_schema::make(sch, t.second, root, uris, known_keywords);
} break;
case json::value_t::array: // "type": ["type1", "type2"]
for (auto &schema_type : attr.value())
for (auto &t : schema_types)
if (t.first == schema_type)
type_[(uint8_t) t.second] = type_schema::make(sch, t.second, root, uris, known_keywords);
type_[static_cast<uint8_t>(t.second)] = type_schema::make(sch, t.second, root, uris, known_keywords);
break;
default:
@ -551,9 +614,10 @@ public:
sch.erase(attr);
}
const auto defaultAttr = sch.find("default");
if (defaultAttr != sch.end()) {
defaultValue_ = defaultAttr.value();
attr = sch.find("default");
if (attr != sch.end()) {
set_default_value(attr.value());
sch.erase(attr);
}
for (auto &key : known_keywords)
@ -561,16 +625,16 @@ public:
// with nlohmann::json float instance (but number in schema-definition) can be seen as unsigned or integer -
// reuse the number-validator for integer values as well, if they have not been specified explicitly
if (type_[(uint8_t) json::value_t::number_float] && !type_[(uint8_t) json::value_t::number_integer])
type_[(uint8_t) json::value_t::number_integer] = type_[(uint8_t) json::value_t::number_float];
if (type_[static_cast<uint8_t>(json::value_t::number_float)] && !type_[static_cast<uint8_t>(json::value_t::number_integer)])
type_[static_cast<uint8_t>(json::value_t::number_integer)] = type_[static_cast<uint8_t>(json::value_t::number_float)];
// #54: JSON-schema does not differentiate between unsigned and signed integer - nlohmann::json does
// we stick with JSON-schema: use the integer-validator if instance-value is unsigned
type_[(uint8_t) json::value_t::number_unsigned] = type_[(uint8_t) json::value_t::number_integer];
type_[static_cast<uint8_t>(json::value_t::number_unsigned)] = type_[static_cast<uint8_t>(json::value_t::number_integer)];
// special for binary types
if (type_[(uint8_t) json::value_t::string]) {
type_[(uint8_t) json::value_t::binary] = type_[(uint8_t) json::value_t::string];
if (type_[static_cast<uint8_t>(json::value_t::string)]) {
type_[static_cast<uint8_t>(json::value_t::binary)] = type_[static_cast<uint8_t>(json::value_t::string)];
}
attr = sch.find("enum");
@ -657,7 +721,7 @@ class string : public schema
void validate(const json::json_pointer &ptr, const json &instance, json_patch &, error_handler &e) const override
{
if (minLength_.first) {
if (utf8_length(instance) < minLength_.second) {
if (utf8_length(instance.get<std::string>()) < minLength_.second) {
std::ostringstream s;
s << "instance is too short as per minLength:" << minLength_.second;
e.error(ptr, instance, s.str());
@ -665,7 +729,7 @@ class string : public schema
}
if (maxLength_.first) {
if (utf8_length(instance) > maxLength_.second) {
if (utf8_length(instance.get<std::string>()) > maxLength_.second) {
std::ostringstream s;
s << "instance is too long as per maxLength: " << maxLength_.second;
e.error(ptr, instance, s.str());
@ -701,7 +765,7 @@ class string : public schema
e.error(ptr, instance, std::string("a format checker was not provided but a format keyword for this string is present: ") + format_.second);
else {
try {
root_->format_check()(format_.second, instance);
root_->format_check()(format_.second, instance.get<std::string>());
} catch (const std::exception &ex) {
e.error(ptr, instance, std::string("format-checking failed: ") + ex.what());
}
@ -715,13 +779,13 @@ public:
{
auto attr = sch.find("maxLength");
if (attr != sch.end()) {
maxLength_ = {true, attr.value()};
maxLength_ = {true, attr.value().get<size_t>()};
sch.erase(attr);
}
attr = sch.find("minLength");
if (attr != sch.end()) {
minLength_ = {true, attr.value()};
minLength_ = {true, attr.value().get<size_t>()};
sch.erase(attr);
}
@ -759,7 +823,7 @@ public:
#ifndef NO_STD_REGEX
attr = sch.find("pattern");
if (attr != sch.end()) {
patternString_ = attr.value();
patternString_ = attr.value().get<std::string>();
pattern_ = {true, REGEX_NAMESPACE::regex(attr.value().get<std::string>(),
REGEX_NAMESPACE::regex::ECMAScript)};
sch.erase(attr);
@ -771,7 +835,7 @@ public:
if (root_->format_check() == nullptr)
throw std::invalid_argument{"a format checker was not provided but a format keyword for this string is present: " + format_.second};
format_ = {true, attr.value()};
format_ = {true, attr.value().get<std::string>()};
sch.erase(attr);
}
}
@ -792,7 +856,7 @@ class numeric : public schema
bool violates_multiple_of(T x) const
{
double res = std::remainder(x, multipleOf_.second);
double eps = std::nextafter(x, 0) - x;
double eps = std::nextafter(x, 0) - static_cast<double>(x);
return std::fabs(res) > std::fabs(eps);
}
@ -804,15 +868,19 @@ class numeric : public schema
if (violates_multiple_of(value))
e.error(ptr, instance, "instance is not a multiple of " + std::to_string(multipleOf_.second));
if (maximum_.first)
if ((exclusiveMaximum_ && value >= maximum_.second) ||
value > maximum_.second)
if (maximum_.first) {
if (exclusiveMaximum_ && value >= maximum_.second)
e.error(ptr, instance, "instance exceeds or equals maximum of " + std::to_string(maximum_.second));
else if (value > maximum_.second)
e.error(ptr, instance, "instance exceeds maximum of " + std::to_string(maximum_.second));
}
if (minimum_.first)
if ((exclusiveMinimum_ && value <= minimum_.second) ||
value < minimum_.second)
if (minimum_.first) {
if (exclusiveMinimum_ && value <= minimum_.second)
e.error(ptr, instance, "instance is below or equals minimum of " + std::to_string(minimum_.second));
else if (value < minimum_.second)
e.error(ptr, instance, "instance is below minimum of " + std::to_string(minimum_.second));
}
}
public:
@ -821,33 +889,33 @@ public:
{
auto attr = sch.find("maximum");
if (attr != sch.end()) {
maximum_ = {true, attr.value()};
maximum_ = {true, attr.value().get<T>()};
kw.insert("maximum");
}
attr = sch.find("minimum");
if (attr != sch.end()) {
minimum_ = {true, attr.value()};
minimum_ = {true, attr.value().get<T>()};
kw.insert("minimum");
}
attr = sch.find("exclusiveMaximum");
if (attr != sch.end()) {
exclusiveMaximum_ = true;
maximum_ = {true, attr.value()};
maximum_ = {true, attr.value().get<T>()};
kw.insert("exclusiveMaximum");
}
attr = sch.find("exclusiveMinimum");
if (attr != sch.end()) {
minimum_ = {true, attr.value()};
exclusiveMinimum_ = true;
minimum_ = {true, attr.value().get<T>()};
kw.insert("exclusiveMinimum");
}
attr = sch.find("multipleOf");
if (attr != sch.end()) {
multipleOf_ = {true, attr.value()};
multipleOf_ = {true, attr.value().get<json::number_float_t>()};
kw.insert("multipleOf");
}
}
@ -882,8 +950,8 @@ class boolean : public schema
{
if (!true_) { // false schema
// empty array
//switch (instance.type()) {
//case json::value_t::array:
// switch (instance.type()) {
// case json::value_t::array:
// if (instance.size() != 0) // valid false-schema
// e.error(ptr, instance, "false-schema required empty array");
// return;
@ -977,9 +1045,9 @@ class object : public schema
for (auto const &prop : properties_) {
const auto finding = instance.find(prop.first);
if (instance.end() == finding) { // if the prop is not in the instance
const auto &defaultValue = prop.second->defaultValue(ptr, instance, e);
if (!defaultValue.is_null()) { // if default value is available
patch.add((ptr / prop.first), defaultValue);
const auto &default_value = prop.second->default_value(ptr, instance, e);
if (!default_value.is_null()) { // if default value is available
patch.add((ptr / prop.first), default_value);
}
}
}
@ -999,13 +1067,13 @@ public:
{
auto attr = sch.find("maxProperties");
if (attr != sch.end()) {
maxProperties_ = {true, attr.value()};
maxProperties_ = {true, attr.value().get<size_t>()};
sch.erase(attr);
}
attr = sch.find("minProperties");
if (attr != sch.end()) {
minProperties_ = {true, attr.value()};
minProperties_ = {true, attr.value().get<size_t>()};
sch.erase(attr);
}
@ -1143,19 +1211,19 @@ public:
{
auto attr = sch.find("maxItems");
if (attr != sch.end()) {
maxItems_ = {true, attr.value()};
maxItems_ = {true, attr.value().get<size_t>()};
sch.erase(attr);
}
attr = sch.find("minItems");
if (attr != sch.end()) {
minItems_ = {true, attr.value()};
minItems_ = {true, attr.value().get<size_t>()};
sch.erase(attr);
}
attr = sch.find("uniqueItems");
if (attr != sch.end()) {
uniqueItems_ = attr.value();
uniqueItems_ = attr.value().get<bool>();
sch.erase(attr);
}
@ -1255,7 +1323,7 @@ std::shared_ptr<schema> schema::make(json &schema,
if (std::find(uris.begin(),
uris.end(),
attr.value().get<std::string>()) == uris.end())
uris.push_back(uris.back().derive(attr.value())); // so add it to the list if it is not there already
uris.push_back(uris.back().derive(attr.value().get<std::string>())); // so add it to the list if it is not there already
schema.erase(attr);
}
@ -1270,15 +1338,25 @@ std::shared_ptr<schema> schema::make(json &schema,
if (attr != schema.end()) { // this schema is a reference
// the last one on the uri-stack is the last id seen before coming here,
// so this is the origial URI for this reference, the $ref-value has thus be resolved from it
auto id = uris.back().derive(attr.value());
auto id = uris.back().derive(attr.value().get<std::string>());
sch = root->get_or_create_ref(id);
schema.erase(attr);
// special case where we break draft-7 and allow overriding of properties when a $ref is used
attr = schema.find("default");
if (attr != schema.end()) {
// copy the referenced schema depending on the underlying type and modify the default value
if (auto new_sch = sch->make_for_default_(sch, root, uris, attr.value())) {
sch = new_sch;
}
schema.erase(attr);
}
} else {
sch = std::make_shared<type_schema>(schema, root, uris);
}
schema.erase("$schema");
schema.erase("default");
schema.erase("title");
schema.erase("description");
} else {

View File

@ -61,7 +61,7 @@ protected:
std::tuple<std::string, std::string, std::string, std::string, std::string> as_tuple() const
{
return std::make_tuple(urn_, scheme_, authority_, path_, identifier_ != "" ? identifier_ : pointer_);
return std::make_tuple(urn_, scheme_, authority_, path_, identifier_ != "" ? identifier_ : pointer_.to_string());
}
public:
@ -80,7 +80,7 @@ public:
std::string fragment() const
{
if (identifier_ == "")
return pointer_;
return pointer_.to_string();
else
return identifier_;
}
@ -159,7 +159,7 @@ public:
/**
* Checks validity of JSON schema built-in string format specifiers like 'date-time', 'ipv4', ...
*/
void default_string_format_check(const std::string &format, const std::string &value);
void JSON_SCHEMA_VALIDATOR_API default_string_format_check(const std::string &format, const std::string &value);
class root_schema;

View File

@ -61,28 +61,44 @@ void rfc3339_time_check(const std::string &value)
throw std::invalid_argument(value + " is not a time string according to RFC 3339.");
}
const auto hour = std::stoi(matches[1].str());
const auto minute = std::stoi(matches[2].str());
const auto second = std::stoi(matches[3].str());
auto hour = std::stoi(matches[1].str());
auto minute = std::stoi(matches[2].str());
auto second = std::stoi(matches[3].str());
// const auto secfrac = std::stof( matches[4].str() );
range_check(hour, 0, 23);
range_check(minute, 0, 59);
/**
* @todo Could be made more exact by querying a leap second database and choosing the
* correct maximum in {58,59,60}. This current solution might match some invalid dates
* but it won't lead to false negatives. This only works if we know the full date, however
*/
range_check(second, 0, 60);
int offsetHour = 0,
offsetMinute = 0;
/* don't check the numerical offset if time zone is specified as 'Z' */
if (!matches[5].str().empty()) {
const auto offsetHour = std::stoi(matches[5].str());
const auto offsetMinute = std::stoi(matches[6].str());
offsetHour = std::stoi(matches[5].str());
offsetMinute = std::stoi(matches[6].str());
range_check(offsetHour, -23, 23);
range_check(offsetMinute, 0, 59);
if (offsetHour < 0)
offsetMinute *= -1;
}
/**
* @todo Could be made more exact by querying a leap second database and choosing the
* correct maximum in {58,59,60}. This current solution might match some invalid dates
* but it won't lead to false negatives. This only works if we know the full date, however
*/
auto day_minutes = hour * 60 + minute - (offsetHour * 60 + offsetMinute);
if (day_minutes < 0)
day_minutes += 60 * 24;
hour = day_minutes % 24;
minute = day_minutes / 24;
if (hour == 23 && minute == 59)
range_check(second, 0, 60); // possible leap-second
else
range_check(second, 0, 59);
}
/**
@ -110,7 +126,7 @@ void rfc3339_time_check(const std::string &value)
* @endverbatim
* NOTE: Per [ABNF] and ISO8601, the "T" and "Z" characters in this
* syntax may alternatively be lower case "t" or "z" respectively.
*/
*/
void rfc3339_date_time_check(const std::string &value)
{
const static std::regex dateTimeRegex{R"(^([0-9]{4}\-[0-9]{2}\-[0-9]{2})[Tt]([0-9]{2}\:[0-9]{2}\:[0-9]{2}(?:\.[0-9]+)?(?:[Zz]|(?:\+|\-)[0-9]{2}\:[0-9]{2}))$)"};
@ -124,7 +140,7 @@ void rfc3339_date_time_check(const std::string &value)
rfc3339_time_check(matches[2].str());
}
const std::string decOctet{R"((?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))"}; // matches numbers 0-255
const std::string decOctet{R"((?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9]))"}; // matches numbers 0-255
const std::string ipv4Address{"(?:" + decOctet + R"(\.){3})" + decOctet};
const std::string h16{R"([0-9A-Fa-f]{1,4})"};
const std::string h16Left{"(?:" + h16 + ":)"};
@ -159,6 +175,8 @@ const std::string host{
"|" + regName +
")"};
const std::string uuid{R"([0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12})"};
// from http://stackoverflow.com/questions/106179/regular-expression-to-match-dns-hostname-or-ip-address
const std::string hostname{R"(^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$)"};
@ -247,6 +265,142 @@ const std::string dotAtom{"(?:" + atext + R"(+(?:\.)" + atext + "+)*)"};
const std::string stackoverflowMagicPart{R"((?:[[:alnum:]](?:[[:alnum:]-]*[[:alnum:]])?\.)+)"
R"([[:alnum:]](?:[[:alnum:]-]*[[:alnum:]])?)"};
const std::string email{"(?:" + dotAtom + "|" + quotedString + ")@(?:" + stackoverflowMagicPart + "|" + domainLiteral + ")"};
/**
* @see
*
* @verbatim
* URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
*
* hier-part = "//" authority path-abempty
* / path-absolute
* / path-rootless
* / path-empty
*
* URI-reference = URI / relative-ref
*
* absolute-URI = scheme ":" hier-part [ "?" query ]
*
* relative-ref = relative-part [ "?" query ] [ "#" fragment ]
*
* relative-part = "//" authority path-abempty
* / path-absolute
* / path-noscheme
* / path-empty
*
* scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
*
* authority = [ userinfo "@" ] host [ ":" port ]
* userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
* host = IP-literal / IPv4address / reg-name
* port = *DIGIT
*
* IP-literal = "[" ( IPv6address / IPvFuture ) "]"
*
* IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
*
* IPv6address = 6( h16 ":" ) ls32
* / "::" 5( h16 ":" ) ls32
* / [ h16 ] "::" 4( h16 ":" ) ls32
* / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
* / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
* / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32
* / [ *4( h16 ":" ) h16 ] "::" ls32
* / [ *5( h16 ":" ) h16 ] "::" h16
* / [ *6( h16 ":" ) h16 ] "::"
*
* h16 = 1*4HEXDIG
* ls32 = ( h16 ":" h16 ) / IPv4address
* IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet
* dec-octet = DIGIT ; 0-9
* / %x31-39 DIGIT ; 10-99
* / "1" 2DIGIT ; 100-199
* / "2" %x30-34 DIGIT ; 200-249
* / "25" %x30-35 ; 250-255
*
* reg-name = *( unreserved / pct-encoded / sub-delims )
*
* path = path-abempty ; begins with "/" or is empty
* / path-absolute ; begins with "/" but not "//"
* / path-noscheme ; begins with a non-colon segment
* / path-rootless ; begins with a segment
* / path-empty ; zero characters
*
* path-abempty = *( "/" segment )
* path-absolute = "/" [ segment-nz *( "/" segment ) ]
* path-noscheme = segment-nz-nc *( "/" segment )
* path-rootless = segment-nz *( "/" segment )
* path-empty = 0<pchar>
*
* segment = *pchar
* segment-nz = 1*pchar
* segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" )
* ; non-zero-length segment without any colon ":"
*
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
*
* query = *( pchar / "/" / "?" )
*
* fragment = *( pchar / "/" / "?" )
*
* pct-encoded = "%" HEXDIG HEXDIG
*
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
* reserved = gen-delims / sub-delims
* gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
* / "*" / "+" / "," / ";" / "="
*
* @endverbatim
* @see adapted from: https://github.com/jhermsmeier/uri.regex/blob/master/uri.regex
*
*/
void rfc3986_uri_check(const std::string &value)
{
const static std::string scheme{R"(([A-Za-z][A-Za-z0-9+\-.]*):)"};
const static std::string hierPart{
R"((?:(\/\/)(?:((?:[A-Za-z0-9\-._~!$&'()*+,;=:]|)"
R"(%[0-9A-Fa-f]{2})*)@)?((?:\[(?:(?:(?:(?:[0-9A-Fa-f]{1,4}:){6}|)"
R"(::(?:[0-9A-Fa-f]{1,4}:){5}|)"
R"((?:[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){4}|)"
R"((?:(?:[0-9A-Fa-f]{1,4}:){0,1}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){3}|)"
R"((?:(?:[0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){2}|)"
R"((?:(?:[0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})?::[0-9A-Fa-f]{1,4}:|)"
R"((?:(?:[0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})?::)(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|)"
R"((?:(?:25[0-5]|2[0-4][0-9]|)"
R"([01]?[0-9][0-9]?)\.){3}(?:25[0-5]|)"
R"(2[0-4][0-9]|)"
R"([01]?[0-9][0-9]?))|)"
R"((?:(?:[0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})?::[0-9A-Fa-f]{1,4}|)"
R"((?:(?:[0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})?::)|)"
R"([Vv][0-9A-Fa-f]+\.[A-Za-z0-9\-._~!$&'()*+,;=:]+)\]|)"
R"((?:(?:25[0-5]|)"
R"(2[0-4][0-9]|)"
R"([01]?[0-9][0-9]?)\.){3}(?:25[0-5]|)"
R"(2[0-4][0-9]|)"
R"([01]?[0-9][0-9]?)|)"
R"((?:[A-Za-z0-9\-._~!$&'()*+,;=]|)"
R"(%[0-9A-Fa-f]{2})*))(?::([0-9]*))?((?:\/(?:[A-Za-z0-9\-._~!$&'()*+,;=:@]|)"
R"(%[0-9A-Fa-f]{2})*)*)|)"
R"(\/((?:(?:[A-Za-z0-9\-._~!$&'()*+,;=:@]|)"
R"(%[0-9A-Fa-f]{2})+(?:\/(?:[A-Za-z0-9\-._~!$&'()*+,;=:@]|)"
R"(%[0-9A-Fa-f]{2})*)*)?)|)"
R"(((?:[A-Za-z0-9\-._~!$&'()*+,;=:@]|)"
R"(%[0-9A-Fa-f]{2})+(?:\/(?:[A-Za-z0-9\-._~!$&'()*+,;=:@]|)"
R"(%[0-9A-Fa-f]{2})*)*)|))"};
const static std::string query{R"((?:\?((?:[A-Za-z0-9\-._~!$&'()*+,;=:@\/?]|%[0-9A-Fa-f]{2})*))?)"};
const static std::string fragment{
R"((?:\#((?:[A-Za-z0-9\-._~!$&'()*+,;=:@\/?]|%[0-9A-Fa-f]{2})*))?)"};
const static std::string uriFormat{scheme + hierPart + query + fragment};
const static std::regex uriRegex{uriFormat};
if (!std::regex_match(value, uriRegex)) {
throw std::invalid_argument(value + " is not a URI string according to RFC 3986.");
}
}
} // namespace
namespace nlohmann
@ -268,6 +422,8 @@ void default_string_format_check(const std::string &format, const std::string &v
rfc3339_date_check(value);
} else if (format == "time") {
rfc3339_time_check(value);
} else if (format == "uri") {
rfc3986_uri_check(value);
} else if (format == "email") {
static const std::regex emailRegex{email};
if (!std::regex_match(value, emailRegex)) {
@ -288,6 +444,11 @@ void default_string_format_check(const std::string &format, const std::string &v
if (!std::regex_match(value, ipv6Regex)) {
throw std::invalid_argument(value + " is not an IPv6 string according to RFC 5954.");
}
} else if (format == "uuid") {
static const std::regex uuidRegex{uuid};
if (!std::regex_match(value, uuidRegex)) {
throw std::invalid_argument(value + " is not an uuid string according to RFC 4122.");
}
} else if (format == "regex") {
try {
std::regex re(value, std::regex::ECMAScript);

View File

@ -1,3 +1,7 @@
MIT License
Copyright (c) 2013-2022 Niels Lohmann
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
@ -14,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
SOFTWARE.

View File

@ -0,0 +1,100 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#pragma once
// This file contains all macro definitions affecting or depending on the ABI
#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK
#if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH)
#if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 11 || NLOHMANN_JSON_VERSION_PATCH != 2
#warning "Already included a different version of the library!"
#endif
#endif
#endif
#define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum)
#define NLOHMANN_JSON_VERSION_MINOR 11 // NOLINT(modernize-macro-to-enum)
#define NLOHMANN_JSON_VERSION_PATCH 2 // NOLINT(modernize-macro-to-enum)
#ifndef JSON_DIAGNOSTICS
#define JSON_DIAGNOSTICS 0
#endif
#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
#define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0
#endif
#if JSON_DIAGNOSTICS
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
#else
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS
#endif
#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
#define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp
#else
#define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON
#endif
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
#endif
// Construct the namespace ABI tags component
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) json_abi ## a ## b
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b) \
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b)
#define NLOHMANN_JSON_ABI_TAGS \
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \
NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON)
// Construct the namespace version component
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
_v ## major ## _ ## minor ## _ ## patch
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \
NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch)
#if NLOHMANN_JSON_NAMESPACE_NO_VERSION
#define NLOHMANN_JSON_NAMESPACE_VERSION
#else
#define NLOHMANN_JSON_NAMESPACE_VERSION \
NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \
NLOHMANN_JSON_VERSION_MINOR, \
NLOHMANN_JSON_VERSION_PATCH)
#endif
// Combine namespace components
#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b
#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \
NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b)
#ifndef NLOHMANN_JSON_NAMESPACE
#define NLOHMANN_JSON_NAMESPACE \
nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \
NLOHMANN_JSON_ABI_TAGS, \
NLOHMANN_JSON_NAMESPACE_VERSION)
#endif
#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN
#define NLOHMANN_JSON_NAMESPACE_BEGIN \
namespace nlohmann \
{ \
inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \
NLOHMANN_JSON_ABI_TAGS, \
NLOHMANN_JSON_NAMESPACE_VERSION) \
{
#endif
#ifndef NLOHMANN_JSON_NAMESPACE_END
#define NLOHMANN_JSON_NAMESPACE_END \
} /* namespace (inline namespace) NOLINT(readability/namespace) */ \
} // namespace nlohmann
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,11 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.2
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_
#define INCLUDE_NLOHMANN_JSON_FWD_HPP_
@ -7,13 +15,15 @@
#include <string> // string
#include <vector> // vector
#include <nlohmann/detail/abi_macros.hpp>
/*!
@brief namespace for Niels Lohmann
@see https://github.com/nlohmann
@since version 1.0.0
*/
namespace nlohmann
{
NLOHMANN_JSON_NAMESPACE_BEGIN
/*!
@brief default JSONSerializer template argument
@ -24,6 +34,8 @@ for serialization.
template<typename T = void, typename SFINAE = void>
struct adl_serializer;
/// a class to store JSON values
/// @sa https://json.nlohmann.me/api/basic_json/
template<template<typename U, typename V, typename... Args> class ObjectType =
std::map,
template<typename U, typename... Args> class ArrayType = std::vector,
@ -37,42 +49,26 @@ template<template<typename U, typename V, typename... Args> class ObjectType =
class BinaryType = std::vector<std::uint8_t>>
class basic_json;
/*!
@brief JSON Pointer
A JSON pointer defines a string syntax for identifying a specific value
within a JSON document. It can be used with functions `at` and
`operator[]`. Furthermore, JSON pointers are the base for JSON patches.
@sa [RFC 6901](https://tools.ietf.org/html/rfc6901)
@since version 2.0.0
*/
template<typename BasicJsonType>
/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
/// @sa https://json.nlohmann.me/api/json_pointer/
template<typename RefStringType>
class json_pointer;
/*!
@brief default JSON class
This type is the default specialization of the @ref basic_json class which
uses the standard template types.
@since version 1.0.0
@brief default specialization
@sa https://json.nlohmann.me/api/json/
*/
using json = basic_json<>;
/// @brief a minimal map-like container that preserves insertion order
/// @sa https://json.nlohmann.me/api/ordered_map/
template<class Key, class T, class IgnoredLess, class Allocator>
struct ordered_map;
/*!
@brief ordered JSON class
This type preserves the insertion order of object keys.
@since version 3.9.0
*/
/// @brief specialization that maintains the insertion order of object keys
/// @sa https://json.nlohmann.me/api/ordered_json/
using ordered_json = basic_json<nlohmann::ordered_map>;
} // namespace nlohmann
NLOHMANN_JSON_NAMESPACE_END
#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_