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 )
nlohmann::json& severities =
project->Internals()->at( "/board/design_settings/rule_severities"_json_pointer );
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" );
project->Internals()->erase( m_internals->PointerFromString( bp + "legacy_no_courtyard_defined" ) );
severities.erase( no_courtyard_key );
migrated = true;
}
if( std::optional<bool> v = project->Get<bool>( bp + "legacy_courtyards_overlap" ) )
if( severities.contains( courtyard_overlap_key ) )
{
if( *v )
if( severities[courtyard_overlap_key].get<bool>() )
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" ) );
severities.erase( courtyard_overlap_key );
migrated = true;
}
}
catch( ... )
{
}
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
{
auto target = target_.lock();
if (!default_value_.is_null())
return default_value_;
auto target = target_.lock();
if (target)
return target->defaultValue(ptr, instance, e);
else
return target->default_value(ptr, instance, e);
e.error(ptr, instance, "unresolved or freed schema-reference " + id_);
return EmptyDefault;
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,16 +868,20 @@ 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:
numeric(const json &sch, root_schema *root, std::set<std::string> &kw)
@ -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");
}
}
@ -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);
int offsetHour = 0,
offsetMinute = 0;
/* don't check the numerical offset if time zone is specified as 'Z' */
if (!matches[5].str().empty()) {
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
*/
range_check(second, 0, 60);
/* 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());
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;
range_check(offsetHour, -23, 23);
range_check(offsetMinute, 0, 59);
}
if (hour == 23 && minute == 59)
range_check(second, 0, 60); // possible leap-second
else
range_check(second, 0, 59);
}
/**
@ -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

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_