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> #include <boost/uuid/entropy_error.hpp>
#endif #endif
#include <nlohmann/json.hpp>
#include <cctype> #include <cctype>
#include <mutex> #include <mutex>
@ -336,3 +338,15 @@ wxString KIID_PATH::AsString() const
return path; 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() JSON_SETTINGS_INTERNALS* JSON_SETTINGS::Internals()
{ {
return m_internals.get(); 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 operator=; %ignore operator=;
%ignore to_json;
%ignore from_json;
// headers/imports that must be included in the _wrapper.cpp at top // headers/imports that must be included in the _wrapper.cpp at top
%{ %{

View File

@ -28,6 +28,7 @@
#include <boost/uuid/uuid.hpp> #include <boost/uuid/uuid.hpp>
#include <macros_swig.h> #include <macros_swig.h>
#include <nlohmann/json_fwd.hpp>
#include <string> #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 #endif // KIID_H

View File

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

View File

@ -963,31 +963,40 @@ bool BOARD_DESIGN_SETTINGS::LoadFromFile( const wxString& aDirectory )
return std::string( name.ToUTF8() ); return std::string( name.ToUTF8() );
}; };
std::string bp = "board.design_settings.rule_severities."; const std::string rs = "rule_severities.";
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 =
Set( rs + drcName( DRCE_MISSING_COURTYARD ), "error" ); project->Internals()->at( "/board/design_settings/rule_severities"_json_pointer );
else
Set( rs + drcName( DRCE_MISSING_COURTYARD ), "ignore" );
project->Internals()->erase( m_internals->PointerFromString( bp + "legacy_no_courtyard_defined" ) ); if( severities.contains( no_courtyard_key ) )
migrated = true; {
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;
}
} }
catch( ... )
if( std::optional<bool> v = project->Get<bool>( bp + "legacy_courtyards_overlap" ) )
{ {
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" ) ) if( Contains( "legacy" ) )
{ {
// This defaults to false for new boards, but version 5.1.x and prior kept the fillets // 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" ); std::string theme_ptr( "appearance.color_theme" );
if( !Count( theme_ptr ) ) if( !Contains( theme_ptr ) )
return true; return true;
wxString selected = At( theme_ptr ).get<wxString>(); 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) 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; return *this;
} }
json_patch &json_patch::replace(const json::json_pointer &ptr, json value) 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; return *this;
} }
json_patch &json_patch::remove(const json::json_pointer &ptr) 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; return *this;
} }

View File

@ -14,6 +14,7 @@
#include <memory> #include <memory>
#include <set> #include <set>
#include <sstream> #include <sstream>
#include <string>
using nlohmann::json; using nlohmann::json;
using nlohmann::json_patch; using nlohmann::json_patch;
@ -34,12 +35,21 @@ using namespace nlohmann::json_schema;
namespace namespace
{ {
static const json EmptyDefault = nullptr;
class schema class schema
{ {
protected: protected:
root_schema *root_; 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: public:
virtual ~schema() = default; 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 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, static std::shared_ptr<schema> make(json &schema,
root_schema *root, root_schema *root,
const std::vector<std::string> &key, const std::vector<std::string> &key,
@ -64,6 +76,8 @@ class schema_ref : public schema
{ {
const std::string id_; const std::string id_;
std::weak_ptr<schema> target_; 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 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_); 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(); auto target = target_.lock();
if (target) if (target)
return target->defaultValue(ptr, instance, e); return target->default_value(ptr, instance, e);
else
e.error(ptr, instance, "unresolved or freed schema-reference " + id_);
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: public:
schema_ref(const std::string &id, root_schema *root) schema_ref(const std::string &id, root_schema *root)
: schema(root), id_(id) {} : schema(root), id_(id) {}
const std::string &id() const { return 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 } // namespace
@ -168,7 +205,7 @@ public:
auto fragment = new_uri.pointer(); 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 // 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()) if (unresolved != file.unresolved.end())
schema::make(value, this, {}, {{new_uri}}); schema::make(value, this, {}, {{new_uri}});
else { // no, nothing ref'd it, keep for later 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, // an unknown keyword can only be referenced by a json-pointer,
// not by a plain name fragment // not by a plain name fragment
if (uri.pointer() != "") { if (uri.pointer().to_string() != "") {
try { try {
auto &subschema = file.unknown_keywords.at(uri.pointer()); // null is returned if not existing 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. auto s = schema::make(subschema, this, {}, {{uri}}); // A JSON Schema MUST be an object or a boolean.
@ -272,11 +309,31 @@ public:
break; break;
} while (1); } while (1);
for (const auto &file : files_) for (const auto &file : files_) {
if (file.second.unresolved.size() != 0) 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, '" + throw std::invalid_argument("after all files have been parsed, '" +
(file.first == "" ? "<root>" : file.first) + (file.first == "" ? "<root>" : file.first) +
"' has still undefined references."); "' has still the following undefined references: " + urefs);
}
}
} }
void validate(const json::json_pointer &ptr, 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"); 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: public:
@ -443,7 +500,6 @@ bool logical_combination<oneOf>::is_validate_complete(const json &instance, cons
class type_schema : public schema class type_schema : public schema
{ {
json defaultValue_ = EmptyDefault;
std::vector<std::shared_ptr<schema>> type_; std::vector<std::shared_ptr<schema>> type_;
std::pair<bool, json> enum_, const_; std::pair<bool, json> enum_, const_;
std::vector<std::shared_ptr<schema>> logic_; std::vector<std::shared_ptr<schema>> logic_;
@ -456,15 +512,10 @@ class type_schema : public schema
std::shared_ptr<schema> if_, then_, else_; 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 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 // 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) if (type)
type->validate(ptr, instance, patch, e); 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: public:
type_schema(json &sch, type_schema(json &sch,
root_schema *root, root_schema *root,
const std::vector<nlohmann::json_uri> &uris) 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 // association between JSON-schema-type and NLohmann-types
static const std::vector<std::pair<std::string, json::value_t>> schema_types = { static const std::vector<std::pair<std::string, json::value_t>> schema_types = {
@ -526,7 +589,7 @@ public:
auto attr = sch.find("type"); auto attr = sch.find("type");
if (attr == sch.end()) // no type field means all sub-types possible if (attr == sch.end()) // no type field means all sub-types possible
for (auto &t : schema_types) 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 { else {
switch (attr.value().type()) { // "type": "type" switch (attr.value().type()) { // "type": "type"
@ -534,14 +597,14 @@ public:
auto schema_type = attr.value().get<std::string>(); auto schema_type = attr.value().get<std::string>();
for (auto &t : schema_types) for (auto &t : schema_types)
if (t.first == schema_type) 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; } break;
case json::value_t::array: // "type": ["type1", "type2"] case json::value_t::array: // "type": ["type1", "type2"]
for (auto &schema_type : attr.value()) for (auto &schema_type : attr.value())
for (auto &t : schema_types) for (auto &t : schema_types)
if (t.first == schema_type) 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; break;
default: default:
@ -551,9 +614,10 @@ public:
sch.erase(attr); sch.erase(attr);
} }
const auto defaultAttr = sch.find("default"); attr = sch.find("default");
if (defaultAttr != sch.end()) { if (attr != sch.end()) {
defaultValue_ = defaultAttr.value(); set_default_value(attr.value());
sch.erase(attr);
} }
for (auto &key : known_keywords) 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 - // 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 // 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]) if (type_[static_cast<uint8_t>(json::value_t::number_float)] && !type_[static_cast<uint8_t>(json::value_t::number_integer)])
type_[(uint8_t) json::value_t::number_integer] = type_[(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_float)];
// #54: JSON-schema does not differentiate between unsigned and signed integer - nlohmann::json does // #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 // 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 // special for binary types
if (type_[(uint8_t) json::value_t::string]) { if (type_[static_cast<uint8_t>(json::value_t::string)]) {
type_[(uint8_t) json::value_t::binary] = type_[(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"); 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 void validate(const json::json_pointer &ptr, const json &instance, json_patch &, error_handler &e) const override
{ {
if (minLength_.first) { if (minLength_.first) {
if (utf8_length(instance) < minLength_.second) { if (utf8_length(instance.get<std::string>()) < minLength_.second) {
std::ostringstream s; std::ostringstream s;
s << "instance is too short as per minLength:" << minLength_.second; s << "instance is too short as per minLength:" << minLength_.second;
e.error(ptr, instance, s.str()); e.error(ptr, instance, s.str());
@ -665,7 +729,7 @@ class string : public schema
} }
if (maxLength_.first) { if (maxLength_.first) {
if (utf8_length(instance) > maxLength_.second) { if (utf8_length(instance.get<std::string>()) > maxLength_.second) {
std::ostringstream s; std::ostringstream s;
s << "instance is too long as per maxLength: " << maxLength_.second; s << "instance is too long as per maxLength: " << maxLength_.second;
e.error(ptr, instance, s.str()); 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); e.error(ptr, instance, std::string("a format checker was not provided but a format keyword for this string is present: ") + format_.second);
else { else {
try { try {
root_->format_check()(format_.second, instance); root_->format_check()(format_.second, instance.get<std::string>());
} catch (const std::exception &ex) { } catch (const std::exception &ex) {
e.error(ptr, instance, std::string("format-checking failed: ") + ex.what()); e.error(ptr, instance, std::string("format-checking failed: ") + ex.what());
} }
@ -715,13 +779,13 @@ public:
{ {
auto attr = sch.find("maxLength"); auto attr = sch.find("maxLength");
if (attr != sch.end()) { if (attr != sch.end()) {
maxLength_ = {true, attr.value()}; maxLength_ = {true, attr.value().get<size_t>()};
sch.erase(attr); sch.erase(attr);
} }
attr = sch.find("minLength"); attr = sch.find("minLength");
if (attr != sch.end()) { if (attr != sch.end()) {
minLength_ = {true, attr.value()}; minLength_ = {true, attr.value().get<size_t>()};
sch.erase(attr); sch.erase(attr);
} }
@ -759,7 +823,7 @@ public:
#ifndef NO_STD_REGEX #ifndef NO_STD_REGEX
attr = sch.find("pattern"); attr = sch.find("pattern");
if (attr != sch.end()) { if (attr != sch.end()) {
patternString_ = attr.value(); patternString_ = attr.value().get<std::string>();
pattern_ = {true, REGEX_NAMESPACE::regex(attr.value().get<std::string>(), pattern_ = {true, REGEX_NAMESPACE::regex(attr.value().get<std::string>(),
REGEX_NAMESPACE::regex::ECMAScript)}; REGEX_NAMESPACE::regex::ECMAScript)};
sch.erase(attr); sch.erase(attr);
@ -771,7 +835,7 @@ public:
if (root_->format_check() == nullptr) 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}; 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); sch.erase(attr);
} }
} }
@ -792,7 +856,7 @@ class numeric : public schema
bool violates_multiple_of(T x) const bool violates_multiple_of(T x) const
{ {
double res = std::remainder(x, multipleOf_.second); 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); return std::fabs(res) > std::fabs(eps);
} }
@ -804,15 +868,19 @@ class numeric : public schema
if (violates_multiple_of(value)) if (violates_multiple_of(value))
e.error(ptr, instance, "instance is not a multiple of " + std::to_string(multipleOf_.second)); e.error(ptr, instance, "instance is not a multiple of " + std::to_string(multipleOf_.second));
if (maximum_.first) if (maximum_.first) {
if ((exclusiveMaximum_ && value >= maximum_.second) || if (exclusiveMaximum_ && value >= maximum_.second)
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)); e.error(ptr, instance, "instance exceeds maximum of " + std::to_string(maximum_.second));
}
if (minimum_.first) if (minimum_.first) {
if ((exclusiveMinimum_ && value <= minimum_.second) || if (exclusiveMinimum_ && value <= minimum_.second)
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)); e.error(ptr, instance, "instance is below minimum of " + std::to_string(minimum_.second));
}
} }
public: public:
@ -821,33 +889,33 @@ public:
{ {
auto attr = sch.find("maximum"); auto attr = sch.find("maximum");
if (attr != sch.end()) { if (attr != sch.end()) {
maximum_ = {true, attr.value()}; maximum_ = {true, attr.value().get<T>()};
kw.insert("maximum"); kw.insert("maximum");
} }
attr = sch.find("minimum"); attr = sch.find("minimum");
if (attr != sch.end()) { if (attr != sch.end()) {
minimum_ = {true, attr.value()}; minimum_ = {true, attr.value().get<T>()};
kw.insert("minimum"); kw.insert("minimum");
} }
attr = sch.find("exclusiveMaximum"); attr = sch.find("exclusiveMaximum");
if (attr != sch.end()) { if (attr != sch.end()) {
exclusiveMaximum_ = true; exclusiveMaximum_ = true;
maximum_ = {true, attr.value()}; maximum_ = {true, attr.value().get<T>()};
kw.insert("exclusiveMaximum"); kw.insert("exclusiveMaximum");
} }
attr = sch.find("exclusiveMinimum"); attr = sch.find("exclusiveMinimum");
if (attr != sch.end()) { if (attr != sch.end()) {
minimum_ = {true, attr.value()};
exclusiveMinimum_ = true; exclusiveMinimum_ = true;
minimum_ = {true, attr.value().get<T>()};
kw.insert("exclusiveMinimum"); kw.insert("exclusiveMinimum");
} }
attr = sch.find("multipleOf"); attr = sch.find("multipleOf");
if (attr != sch.end()) { if (attr != sch.end()) {
multipleOf_ = {true, attr.value()}; multipleOf_ = {true, attr.value().get<json::number_float_t>()};
kw.insert("multipleOf"); kw.insert("multipleOf");
} }
} }
@ -882,8 +950,8 @@ class boolean : public schema
{ {
if (!true_) { // false schema if (!true_) { // false schema
// empty array // empty array
//switch (instance.type()) { // switch (instance.type()) {
//case json::value_t::array: // case json::value_t::array:
// if (instance.size() != 0) // valid false-schema // if (instance.size() != 0) // valid false-schema
// e.error(ptr, instance, "false-schema required empty array"); // e.error(ptr, instance, "false-schema required empty array");
// return; // return;
@ -977,9 +1045,9 @@ class object : public schema
for (auto const &prop : properties_) { for (auto const &prop : properties_) {
const auto finding = instance.find(prop.first); const auto finding = instance.find(prop.first);
if (instance.end() == finding) { // if the prop is not in the instance if (instance.end() == finding) { // if the prop is not in the instance
const auto &defaultValue = prop.second->defaultValue(ptr, instance, e); const auto &default_value = prop.second->default_value(ptr, instance, e);
if (!defaultValue.is_null()) { // if default value is available if (!default_value.is_null()) { // if default value is available
patch.add((ptr / prop.first), defaultValue); patch.add((ptr / prop.first), default_value);
} }
} }
} }
@ -999,13 +1067,13 @@ public:
{ {
auto attr = sch.find("maxProperties"); auto attr = sch.find("maxProperties");
if (attr != sch.end()) { if (attr != sch.end()) {
maxProperties_ = {true, attr.value()}; maxProperties_ = {true, attr.value().get<size_t>()};
sch.erase(attr); sch.erase(attr);
} }
attr = sch.find("minProperties"); attr = sch.find("minProperties");
if (attr != sch.end()) { if (attr != sch.end()) {
minProperties_ = {true, attr.value()}; minProperties_ = {true, attr.value().get<size_t>()};
sch.erase(attr); sch.erase(attr);
} }
@ -1143,19 +1211,19 @@ public:
{ {
auto attr = sch.find("maxItems"); auto attr = sch.find("maxItems");
if (attr != sch.end()) { if (attr != sch.end()) {
maxItems_ = {true, attr.value()}; maxItems_ = {true, attr.value().get<size_t>()};
sch.erase(attr); sch.erase(attr);
} }
attr = sch.find("minItems"); attr = sch.find("minItems");
if (attr != sch.end()) { if (attr != sch.end()) {
minItems_ = {true, attr.value()}; minItems_ = {true, attr.value().get<size_t>()};
sch.erase(attr); sch.erase(attr);
} }
attr = sch.find("uniqueItems"); attr = sch.find("uniqueItems");
if (attr != sch.end()) { if (attr != sch.end()) {
uniqueItems_ = attr.value(); uniqueItems_ = attr.value().get<bool>();
sch.erase(attr); sch.erase(attr);
} }
@ -1255,7 +1323,7 @@ std::shared_ptr<schema> schema::make(json &schema,
if (std::find(uris.begin(), if (std::find(uris.begin(),
uris.end(), uris.end(),
attr.value().get<std::string>()) == 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); schema.erase(attr);
} }
@ -1270,15 +1338,25 @@ std::shared_ptr<schema> schema::make(json &schema,
if (attr != schema.end()) { // this schema is a reference if (attr != schema.end()) { // this schema is a reference
// the last one on the uri-stack is the last id seen before coming here, // 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 // 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); sch = root->get_or_create_ref(id);
schema.erase(attr); 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 { } else {
sch = std::make_shared<type_schema>(schema, root, uris); sch = std::make_shared<type_schema>(schema, root, uris);
} }
schema.erase("$schema"); schema.erase("$schema");
schema.erase("default");
schema.erase("title"); schema.erase("title");
schema.erase("description"); schema.erase("description");
} else { } else {

View File

@ -61,7 +61,7 @@ protected:
std::tuple<std::string, std::string, std::string, std::string, std::string> as_tuple() const 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: public:
@ -80,7 +80,7 @@ public:
std::string fragment() const std::string fragment() const
{ {
if (identifier_ == "") if (identifier_ == "")
return pointer_; return pointer_.to_string();
else else
return identifier_; return identifier_;
} }
@ -159,7 +159,7 @@ public:
/** /**
* Checks validity of JSON schema built-in string format specifiers like 'date-time', 'ipv4', ... * 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; 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."); throw std::invalid_argument(value + " is not a time string according to RFC 3339.");
} }
const auto hour = std::stoi(matches[1].str()); auto hour = std::stoi(matches[1].str());
const auto minute = std::stoi(matches[2].str()); auto minute = std::stoi(matches[2].str());
const auto second = std::stoi(matches[3].str()); auto second = std::stoi(matches[3].str());
// const auto secfrac = std::stof( matches[4].str() ); // const auto secfrac = std::stof( matches[4].str() );
range_check(hour, 0, 23); range_check(hour, 0, 23);
range_check(minute, 0, 59); range_check(minute, 0, 59);
/**
* @todo Could be made more exact by querying a leap second database and choosing the int offsetHour = 0,
* correct maximum in {58,59,60}. This current solution might match some invalid dates offsetMinute = 0;
* 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' */ /* don't check the numerical offset if time zone is specified as 'Z' */
if (!matches[5].str().empty()) { if (!matches[5].str().empty()) {
const auto offsetHour = std::stoi(matches[5].str()); offsetHour = std::stoi(matches[5].str());
const auto offsetMinute = std::stoi(matches[6].str()); offsetMinute = std::stoi(matches[6].str());
range_check(offsetHour, -23, 23); range_check(offsetHour, -23, 23);
range_check(offsetMinute, 0, 59); 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 * @endverbatim
* NOTE: Per [ABNF] and ISO8601, the "T" and "Z" characters in this * NOTE: Per [ABNF] and ISO8601, the "T" and "Z" characters in this
* syntax may alternatively be lower case "t" or "z" respectively. * syntax may alternatively be lower case "t" or "z" respectively.
*/ */
void rfc3339_date_time_check(const std::string &value) 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}))$)"}; 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()); 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 ipv4Address{"(?:" + decOctet + R"(\.){3})" + decOctet};
const std::string h16{R"([0-9A-Fa-f]{1,4})"}; const std::string h16{R"([0-9A-Fa-f]{1,4})"};
const std::string h16Left{"(?:" + h16 + ":)"}; const std::string h16Left{"(?:" + h16 + ":)"};
@ -159,6 +175,8 @@ const std::string host{
"|" + regName + "|" + 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 // 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]))*$)"}; 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:]])?\.)+)" const std::string stackoverflowMagicPart{R"((?:[[:alnum:]](?:[[:alnum:]-]*[[:alnum:]])?\.)+)"
R"([[:alnum:]](?:[[:alnum:]-]*[[:alnum:]])?)"}; R"([[:alnum:]](?:[[:alnum:]-]*[[:alnum:]])?)"};
const std::string email{"(?:" + dotAtom + "|" + quotedString + ")@(?:" + stackoverflowMagicPart + "|" + domainLiteral + ")"}; 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
namespace nlohmann namespace nlohmann
@ -268,6 +422,8 @@ void default_string_format_check(const std::string &format, const std::string &v
rfc3339_date_check(value); rfc3339_date_check(value);
} else if (format == "time") { } else if (format == "time") {
rfc3339_time_check(value); rfc3339_time_check(value);
} else if (format == "uri") {
rfc3986_uri_check(value);
} else if (format == "email") { } else if (format == "email") {
static const std::regex emailRegex{email}; static const std::regex emailRegex{email};
if (!std::regex_match(value, emailRegex)) { 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)) { if (!std::regex_match(value, ipv6Regex)) {
throw std::invalid_argument(value + " is not an IPv6 string according to RFC 5954."); 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") { } else if (format == "regex") {
try { try {
std::regex re(value, std::regex::ECMAScript); 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 Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 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_ #ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_
#define INCLUDE_NLOHMANN_JSON_FWD_HPP_ #define INCLUDE_NLOHMANN_JSON_FWD_HPP_
@ -7,13 +15,15 @@
#include <string> // string #include <string> // string
#include <vector> // vector #include <vector> // vector
#include <nlohmann/detail/abi_macros.hpp>
/*! /*!
@brief namespace for Niels Lohmann @brief namespace for Niels Lohmann
@see https://github.com/nlohmann @see https://github.com/nlohmann
@since version 1.0.0 @since version 1.0.0
*/ */
namespace nlohmann NLOHMANN_JSON_NAMESPACE_BEGIN
{
/*! /*!
@brief default JSONSerializer template argument @brief default JSONSerializer template argument
@ -24,6 +34,8 @@ for serialization.
template<typename T = void, typename SFINAE = void> template<typename T = void, typename SFINAE = void>
struct adl_serializer; 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 = template<template<typename U, typename V, typename... Args> class ObjectType =
std::map, std::map,
template<typename U, typename... Args> class ArrayType = std::vector, 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 BinaryType = std::vector<std::uint8_t>>
class basic_json; class basic_json;
/*! /// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
@brief JSON Pointer /// @sa https://json.nlohmann.me/api/json_pointer/
template<typename RefStringType>
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>
class json_pointer; class json_pointer;
/*! /*!
@brief default JSON class @brief default specialization
@sa https://json.nlohmann.me/api/json/
This type is the default specialization of the @ref basic_json class which
uses the standard template types.
@since version 1.0.0
*/ */
using json = basic_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> template<class Key, class T, class IgnoredLess, class Allocator>
struct ordered_map; struct ordered_map;
/*! /// @brief specialization that maintains the insertion order of object keys
@brief ordered JSON class /// @sa https://json.nlohmann.me/api/ordered_json/
This type preserves the insertion order of object keys.
@since version 3.9.0
*/
using ordered_json = basic_json<nlohmann::ordered_map>; using ordered_json = basic_json<nlohmann::ordered_map>;
} // namespace nlohmann NLOHMANN_JSON_NAMESPACE_END
#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ #endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_