Compare commits

..
Author SHA1 Message Date
Niels Lohmann de261db5ae Merge branch 'develop' into claude/pr-3899-merge-requirements-e1932e
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-27 20:58:14 +02:00
Niels Lohmann 013923dc89 Add as_base_class to the docset search index
New public members get an entry in docs/docset/docSet.sql (as done for
to_bon8/from_bon8 in #2998). Without it, the Dash/Zeal docset built from
the documentation cannot find basic_json::as_base_class.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-27 18:19:44 +02:00
Niels Lohmann 82601da6ec Merge branch 'develop' into as_base_class
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-27 16:56:59 +02:00
Niels LohmannandRaphael Grimm 1cd636bd61 Add basic_json::as_base_class and document name conflicts with custom base classes
Members of basic_json hide members of a custom base class with the same
name, and future releases may add members that hide ones accessible
today. Document this in json_base_class_t and add as_base_class() to
reach hidden members without spelling out the cast.

Also make json_base_class_t a public member type. It was documented
since 3.12.0, but declared private, so users could not name it.

Supersedes #3899.

Co-authored-by: Raphael Grimm <1005058+barcode@users.noreply.github.com>
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-27 14:53:50 +02:00
30 changed files with 233 additions and 4656 deletions
+1
View File
@@ -16,6 +16,7 @@ INSERT INTO searchIndex(name, type, path) VALUES ('basic_json', 'Class', 'api/ba
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::accept', 'Function', 'api/basic_json/accept/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::array', 'Function', 'api/basic_json/array/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::array_t', 'Type', 'api/basic_json/array_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::as_base_class', 'Method', 'api/basic_json/as_base_class/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::at', 'Method', 'api/basic_json/at/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::back', 'Method', 'api/basic_json/back/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::basic_json', 'Constructor', 'api/basic_json/basic_json/index.html');
@@ -0,0 +1,53 @@
# <small>nlohmann::basic_json::</small>as_base_class
```cpp
json_base_class_t& as_base_class() noexcept;
const json_base_class_t& as_base_class() const noexcept;
```
Returns a reference to this object as its custom base class [`json_base_class_t`](json_base_class_t.md). No copy is
made.
Since `basic_json` derives from `json_base_class_t`, a member of `basic_json` hides any member of the custom base class
with the same name. This function makes such hidden members accessible again.
## Return value
reference to this object as [`json_base_class_t`](json_base_class_t.md)
## Exception safety
No-throw guarantee: this function never throws exceptions.
## Complexity
Constant.
## Notes
The function is equivalent to `static_cast<json_base_class_t&>(j)` (or `static_cast<const json_base_class_t&>(j)`).
## Examples
??? example
The example shows how to use `as_base_class` to access members of the custom base class that are hidden by members
of `basic_json`.
```cpp
--8<-- "examples/as_base_class.cpp"
```
Output:
```json
--8<-- "examples/as_base_class.output"
```
## See also
- [json_base_class_t](json_base_class_t.md) - type of the custom base class
## Version history
- Added in version 3.13.0.
+1
View File
@@ -200,6 +200,7 @@ Direct access to the stored value of a JSON value.
- [**get_ref**](get_ref.md) - get a reference value
- [**operator ValueType**](operator_ValueType.md) - get a value
- [**get_binary**](get_binary.md) - get a binary value
- [**as_base_class**](as_base_class.md) - access the custom base class
### Element access
@@ -27,6 +27,18 @@ A `CustomBaseClass` with non-static data members forfeits `basic_json`'s
[standard layout](https://en.cppreference.com/w/cpp/named_req/StandardLayoutType) guarantee. See
[Template Parameter Requirements](../../features/types/template_parameters.md#custombaseclass).
#### Name conflicts
Since `basic_json` derives from `CustomBaseClass`, members of `basic_json` hide members of `CustomBaseClass` with the
same name. Hidden members remain accessible via [`as_base_class`](as_base_class.md) or by casting the value to
`json_base_class_t`.
!!! warning "Avoid generic member names"
Future versions of the library may add members to `basic_json` that hide members of `CustomBaseClass` that are
accessible today. To reduce the risk of such conflicts, avoid generic names for the members of `CustomBaseClass`,
for instance by using a distinctive prefix.
## Examples
??? example
@@ -43,6 +55,11 @@ A `CustomBaseClass` with non-static data members forfeits `basic_json`'s
--8<-- "examples/json_base_class_t.output"
```
## See also
- [as_base_class](as_base_class.md) - access the custom base class
## Version history
- Added in version 3.12.0.
- Made a public member type in version 3.13.0; it was private before, so it could not be named outside the class.
-2
View File
@@ -53,8 +53,6 @@ header. See also the [macro overview page](../../features/macros.md).
- [**JSON_BRACE_INIT_COPY_SEMANTICS**](json_brace_init_copy_semantics.md) - opt in to copy/move semantics for single-element brace initialization
- [**JSON_DISABLE_ENUM_SERIALIZATION**](json_disable_enum_serialization.md) - switch off default serialization/deserialization functions for enums
- [**JSON_USE_IMPLICIT_CONVERSIONS**](json_use_implicit_conversions.md) - control implicit conversions
- [**JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS**](json_use_objects_for_enum_keyed_maps.md) - opt in to storing maps with enum
keys as objects
## Comparison behavior
@@ -1,139 +0,0 @@
# JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
```cpp
#define JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS /* value */
```
When defined to `1`, maps whose keys are enums (such as `std::map<E, T>` or `std::unordered_map<E, T>`) are stored as
JSON objects, using the enum's own conversion for the keys. By default, they are stored as arrays of `[key, value]`
pairs.
## Default definition
The default value is `0` (disabled — existing behavior is preserved).
```cpp
#define JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS 0
```
## Notes
!!! note "Background"
JSON object keys are strings, so a map is only stored as an object if its keys can be converted to a string type.
Enums are not, even if [`NLOHMANN_JSON_SERIALIZE_ENUM`](nlohmann_json_serialize_enum.md) maps them to strings, so a
map with enum keys becomes an array of `[key, value]` pairs:
```json
[["stopped", "aa"], ["completed", "bb"]]
```
With this macro, the same map becomes an object
(see [#4378](https://github.com/nlohmann/json/issues/4378)):
```json
{"completed": "bb", "stopped": "aa"}
```
!!! note "Maps with non-unique keys"
Maps that allow duplicate keys, such as `std::multimap<E, T>` or `std::unordered_multimap<E, T>`, are not affected
by the macro and are still stored as arrays of `[key, value]` pairs, as an object cannot hold duplicate keys.
!!! note "Reading"
Reading is not affected by the macro: a map with enum keys can always be read from both an array of pairs and an
object. For the latter, each key is converted to the enum with its `from_json` function, e.g., the one defined by
[`NLOHMANN_JSON_SERIALIZE_ENUM`](nlohmann_json_serialize_enum.md). Data written without the macro can therefore
still be read after enabling it.
!!! warning "Keys must serialize to distinct strings"
Each key is converted with the enum's `to_json` function. If a key is not converted to a string (for instance, an
enum without [`NLOHMANN_JSON_SERIALIZE_ENUM`](nlohmann_json_serialize_enum.md), which is stored as an integer, or an
enumerator mapped to `nullptr`), [`type_error.302`](../../home/exceptions.md#jsonexceptiontype_error302) is thrown.
If two keys are converted to the same string (for instance, because
[`NLOHMANN_JSON_SERIALIZE_ENUM`](nlohmann_json_serialize_enum.md) maps an unlisted enumerator to the first entry),
[`type_error.318`](../../home/exceptions.md#jsonexceptiontype_error318) is thrown. In both cases, the target value
is not changed.
!!! warning "Opt-in only"
This macro must be defined **before** including `<nlohmann/json.hpp>`. Defining it after the include has no effect.
!!! note "ABI compatibility"
The value of this macro is encoded in the [namespace](../../features/namespace.md) (tag `_ekmo`), resulting in
distinct symbol names. Translation units compiled with and without it can therefore be linked into the same program
without One Definition Rule (ODR) violations, but they cannot exchange instances of library types.
## Examples
??? example "Default behavior (macro not defined)"
Without the macro, a map with enum keys is stored as an array of pairs:
```cpp
#include <map>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
enum TaskState { TS_STOPPED, TS_RUNNING, TS_COMPLETED };
NLOHMANN_JSON_SERIALIZE_ENUM(TaskState, {
{TS_STOPPED, "stopped"},
{TS_RUNNING, "running"},
{TS_COMPLETED, "completed"},
})
int main()
{
std::map<TaskState, std::string> m = {{TS_STOPPED, "aa"}, {TS_COMPLETED, "bb"}};
json j = m;
// j is [["stopped","aa"],["completed","bb"]]
}
```
??? example "Objects for enum-keyed maps (macro defined to 1)"
With the macro, the same map is stored as an object:
```cpp
#define JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS 1
#include <map>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
enum TaskState { TS_STOPPED, TS_RUNNING, TS_COMPLETED };
NLOHMANN_JSON_SERIALIZE_ENUM(TaskState, {
{TS_STOPPED, "stopped"},
{TS_RUNNING, "running"},
{TS_COMPLETED, "completed"},
})
int main()
{
std::map<TaskState, std::string> m = {{TS_STOPPED, "aa"}, {TS_COMPLETED, "bb"}};
json j = m;
// j is {"completed":"bb","stopped":"aa"}
auto m2 = j.get<std::map<TaskState, std::string>>();
// m2 == m
}
```
## See also
- [Specializing enum conversion](../../features/enum_conversion.md)
- [**NLOHMANN_JSON_SERIALIZE_ENUM**](nlohmann_json_serialize_enum.md) - serialize/deserialize an enum
- [**NLOHMANN_JSON_SERIALIZE_ENUM_STRICT**](nlohmann_json_serialize_enum_strict.md) - serialize/deserialize an enum with
exceptions
## Version history
- Added in version 3.13.0.
@@ -41,9 +41,6 @@ inline void from_json(const BasicJsonType& j, type& e);
conversion. Select this default pair carefully. See example 1 below.
- If an enum or JSON value is specified in multiple conversions, the first matching conversion from the top of the
list will be returned when converting to or from JSON. See example 2 below.
- Maps with enum keys (e.g., `std::map<ENUM_TYPE, T>`) are stored as arrays of `[key, value]` pairs by default.
Define [`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](json_use_objects_for_enum_keyed_maps.md) to store them as objects
with the converted keys. Such maps can be read from both forms.
## Examples
@@ -83,7 +80,6 @@ inline void from_json(const BasicJsonType& j, type& e);
- [Specializing enum conversion](../../features/enum_conversion.md)
- [`NLOHMANN_JSON_SERIALIZE_ENUM_STRICT`](./nlohmann_json_serialize_enum_strict.md)
- [`JSON_DISABLE_ENUM_SERIALIZATION`](json_disable_enum_serialization.md)
- [`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](json_use_objects_for_enum_keyed_maps.md)
## Version history
@@ -44,9 +44,6 @@ inline void from_json(const BasicJsonType& j, type& e);
`"enum value out of range for <type>"`.
- If an enum or JSON value is specified in multiple conversions, the first matching conversion from the top of the
list will be returned when converting to or from JSON. See example 2 below.
- Maps with enum keys (e.g., `std::map<ENUM_TYPE, T>`) are stored as arrays of `[key, value]` pairs by default.
Define [`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](json_use_objects_for_enum_keyed_maps.md) to store them as objects
with the converted keys. Such maps can be read from both forms.
## Examples
@@ -102,7 +99,6 @@ inline void from_json(const BasicJsonType& j, type& e);
- [Specializing enum conversion](../../features/enum_conversion.md)
- [`NLOHMANN_JSON_SERIALIZE_ENUM`](./nlohmann_json_serialize_enum.md)
- [`JSON_DISABLE_ENUM_SERIALIZATION`](json_disable_enum_serialization.md)
- [`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](json_use_objects_for_enum_keyed_maps.md)
## Version history
@@ -0,0 +1,41 @@
#include <iostream>
#include <nlohmann/json.hpp>
class base_class_with_hidden_members
{
public:
const char* type_name() const noexcept
{
return "my_type_name";
}
std::size_t size() const noexcept
{
return 42;
}
};
using json = nlohmann::basic_json <
std::map,
std::vector,
std::string,
bool,
std::int64_t,
std::uint64_t,
double,
std::allocator,
nlohmann::adl_serializer,
std::vector<std::uint8_t>,
base_class_with_hidden_members
>;
int main()
{
json j = {1, 2, 3};
// the members of basic_json hide the members of the base class
std::cout << j.type_name() << ' ' << j.size() << '\n';
// access the hidden members of the base class
std::cout << j.as_base_class().type_name() << ' ' << j.as_base_class().size() << '\n';
}
@@ -0,0 +1,2 @@
array 3
my_type_name 42
@@ -43,23 +43,6 @@ json jPi = 3.14;
assert(jPi.get<TaskState>() == TS_INVALID );
```
## Maps with enum keys
By default, maps with enum keys, such as `std::map<TaskState, std::string>`, are stored as arrays of `[key, value]`
pairs, because JSON object keys must be strings. Define
[`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](../api/macros/json_use_objects_for_enum_keyed_maps.md) before including the
library to store them as objects, with the keys converted by the enum's `to_json()` function:
```cpp
std::map<TaskState, std::string> m = {{TS_STOPPED, "aa"}, {TS_COMPLETED, "bb"}};
json j = m;
// default: [["stopped","aa"],["completed","bb"]]
// with JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS: {"completed":"bb","stopped":"aa"}
```
Either form can be read back, with or without the macro.
## Notes
Just as in [Arbitrary Type Conversions](arbitrary_types.md) above,
-7
View File
@@ -167,13 +167,6 @@ behavior is deprecated and switched off (`0`) by default.
See [full documentation of `JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON`](../api/macros/json_use_legacy_discarded_value_comparison.md).
## `JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`
When defined to `1`, maps with enum keys (e.g., `std::map<E, T>`) are stored as objects, using the enum's conversion for
the keys, instead of arrays of `[key, value]` pairs. It is switched off (`0`) by default.
See [full documentation of `JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](../api/macros/json_use_objects_for_enum_keyed_maps.md).
## `JSON_USE_SIMDUTF`
When defined, UTF-8 validation of JSON strings read from contiguous byte input is delegated to the
-2
View File
@@ -20,8 +20,6 @@ The complete default namespace name is derived as follows:
`_bics`.
- [`JSON_PRECISE_STREAM_POSITION`](../api/macros/json_precise_stream_position.md) defined non-zero appends `_psp`.
- [`JSON_STRICT_NUL_HANDLING`](../api/macros/json_strict_nul_handling.md) defined non-zero appends `_snul`.
- [`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](../api/macros/json_use_objects_for_enum_keyed_maps.md) defined non-zero
appends `_ekmo`.
- The inline namespace ends with the suffix `_v` followed by the 3 components of the version number separated by
underscores. To omit the version component, see [Disabling the version component](#disabling-the-version-component)
below.
-16
View File
@@ -589,9 +589,6 @@ During implicit or explicit value conversion, the JSON type must be compatible w
[json.exception.type_error.302] type must be string, but is object
```
This exception is also thrown with [`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](../api/macros/json_use_objects_for_enum_keyed_maps.md)
if a key of a map with enum keys is not converted to a string, for instance, because the enum is stored as an integer.
### json.exception.type_error.303
To retrieve a reference to a value stored in a `basic_json` object with `get_ref`, the type of the reference must match the value type. For instance, for a JSON array, the `ReferenceType` must be `array_t &`.
@@ -778,19 +775,6 @@ The dynamic type of the object cannot be represented in the requested serializat
Encapsulate the JSON value in an object. That is, instead of serializing `#!json true`, serialize `#!json {"value": true}`
### json.exception.type_error.318
With [`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](../api/macros/json_use_objects_for_enum_keyed_maps.md), a map with enum
keys is stored as an object. This exception is thrown if two of its keys are converted to the same string, so one of the
entries would be lost. This happens, for instance, if [`NLOHMANN_JSON_SERIALIZE_ENUM`](../api/macros/nlohmann_json_serialize_enum.md)
does not list an enumerator and it is therefore converted like the first listed one.
!!! failure "Example message"
```
[json.exception.type_error.318] duplicate object key 'red'
```
## Out of range
This exception is thrown in case a library function is called on an input parameter that exceeds the expected range, for instance, in the case of array indices or nonexisting object keys.
+1 -1
View File
@@ -114,6 +114,7 @@ nav:
- 'accept': api/basic_json/accept.md
- 'array': api/basic_json/array.md
- 'array_t': api/basic_json/array_t.md
- 'as_base_class': api/basic_json/as_base_class.md
- 'at': api/basic_json/at.md
- 'back': api/basic_json/back.md
- 'begin': api/basic_json/begin.md
@@ -303,7 +304,6 @@ nav:
- 'JSON_USE_GLOBAL_UDLS': api/macros/json_use_global_udls.md
- 'JSON_USE_IMPLICIT_CONVERSIONS': api/macros/json_use_implicit_conversions.md
- 'JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON': api/macros/json_use_legacy_discarded_value_comparison.md
- 'JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS': api/macros/json_use_objects_for_enum_keyed_maps.md
- 'JSON_USE_SIMDUTF': api/macros/json_use_simdutf.md
- 'NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE, NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT, NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE, NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE, NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT, NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE': api/macros/nlohmann_define_derived_type.md
- 'NLOHMANN_DEFINE_TYPE_INTRUSIVE, NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT, NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE': api/macros/nlohmann_define_type_intrusive.md
+4 -15
View File
@@ -46,10 +46,6 @@
#define JSON_STRICT_NUL_HANDLING 0
#endif
#ifndef JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
#define JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS 0
#endif
#if JSON_DIAGNOSTICS
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
#else
@@ -86,20 +82,14 @@
#define NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING
#endif
#if JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
#define NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS _ekmo
#else
#define NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS
#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, c, d, e, f, g) json_abi ## a ## b ## c ## d ## e ## f ## g
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c, d, e, f, g) \
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g)
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f) json_abi ## a ## b ## c ## d ## e ## f
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c, d, e, f) \
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f)
#define NLOHMANN_JSON_ABI_TAGS \
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
@@ -108,8 +98,7 @@
NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS, \
NLOHMANN_JSON_ABI_TAG_BRACE_INIT_COPY_SEMANTICS, \
NLOHMANN_JSON_ABI_TAG_PRECISE_STREAM_POSITION, \
NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING, \
NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS)
NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING)
// Construct the namespace version component
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
@@ -550,40 +550,11 @@ auto from_json(BasicJsonType&& j, TupleRelated&& t)
return from_json_tuple_impl(std::forward<BasicJsonType>(j), std::forward<TupleRelated>(t), priority_tag<3> {});
}
// read a map with enum keys from an object, using the enum's own from_json for
// the keys (e.g., from NLOHMANN_JSON_SERIALIZE_ENUM); this is the form written
// with JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
template<typename BasicJsonType, typename Map>
inline bool from_json_enum_keyed_object(const BasicJsonType& j, Map& m, std::true_type /*key is enum*/)
{
if (!j.is_object())
{
return false;
}
m.clear();
for (const auto& p : *j.template get_ptr<const typename BasicJsonType::object_t*>())
{
m.emplace(BasicJsonType(p.first).template get<typename Map::key_type>(), p.second.template get<typename Map::mapped_type>());
}
return true;
}
template<typename BasicJsonType, typename Map>
inline bool from_json_enum_keyed_object(const BasicJsonType& /*j*/, Map& /*m*/, std::false_type /*key is enum*/)
{
return false;
}
template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator,
typename = enable_if_t < !std::is_constructible <
typename BasicJsonType::string_t, Key >::value >>
inline void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m)
{
// NOLINTNEXTLINE(modernize-type-traits) we use C++11
if (from_json_enum_keyed_object(j, m, std::is_enum<Key> {}))
{
return;
}
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
@@ -604,11 +575,6 @@ template < typename BasicJsonType, typename Key, typename Value, typename Hash,
typename BasicJsonType::string_t, Key >::value >>
inline void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m)
{
// NOLINTNEXTLINE(modernize-type-traits) we use C++11
if (from_json_enum_keyed_object(j, m, std::is_enum<Key> {}))
{
return;
}
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
@@ -23,7 +23,6 @@
#include <valarray> // valarray
#include <vector> // vector
#include <nlohmann/detail/exceptions.hpp>
#include <nlohmann/detail/iterators/iteration_proxy.hpp>
#include <nlohmann/detail/meta/cpp_future.hpp>
#include <nlohmann/detail/meta/std_fs.hpp>
@@ -382,9 +381,6 @@ template < typename BasicJsonType, typename CompatibleArrayType,
!is_basic_json<CompatibleArrayType>::value
#if JSON_HAS_RANGES && !defined(__MINGW32__)
&& !is_compatible_range_view<CompatibleArrayType>::value
#endif
#if JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
&& !is_enum_keyed_map<CompatibleArrayType>::value
#endif
,
int > = 0 >
@@ -439,33 +435,6 @@ inline void to_json(BasicJsonType& j, const CompatibleObjectType& obj)
external_constructor<value_t::object>::construct(j, obj);
}
#if JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
// store a map with enum keys as an object, using the enum's own to_json for the
// keys (e.g., from NLOHMANN_JSON_SERIALIZE_ENUM); without the macro, such maps
// are stored as arrays of [key, value] pairs
template < typename BasicJsonType, typename EnumKeyedMap,
enable_if_t < is_enum_keyed_map<EnumKeyedMap>::value&& !is_basic_json<EnumKeyedMap>::value, int > = 0 >
inline void to_json(BasicJsonType& j, const EnumKeyedMap& map)
{
typename BasicJsonType::object_t obj;
for (const auto& p : map)
{
BasicJsonType key = p.first;
if (JSON_HEDLEY_UNLIKELY(!key.is_string()))
{
JSON_THROW(type_error::create(302, concat("type must be string, but is ", key.type_name()), &key));
}
auto& key_string = *key.template get_ptr<typename BasicJsonType::string_t*>();
if (JSON_HEDLEY_UNLIKELY(!obj.emplace(key_string, BasicJsonType(p.second)).second))
{
JSON_THROW(type_error::create(318, concat("duplicate object key '", key_string, "'"), &key));
}
}
external_constructor<value_t::object>::construct(j, std::move(obj));
}
#endif
template<typename BasicJsonType>
inline void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
{
@@ -46,7 +46,6 @@
#undef JSON_BRACE_INIT_COPY_SEMANTICS
#undef JSON_PRECISE_STREAM_POSITION
#undef JSON_STRICT_NUL_HANDLING
#undef JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
#endif
#include <nlohmann/thirdparty/hedley/hedley_undef.hpp>
@@ -400,30 +400,6 @@ template<typename BasicJsonType, typename CompatibleObjectType>
struct is_compatible_object_type
: is_compatible_object_type_impl<BasicJsonType, CompatibleObjectType> {};
template<typename T>
using insert_result_t = decltype(std::declval<T&>().insert(std::declval<const value_type_t<T>&>()));
template<typename T>
using insert_result_second_t = decltype(std::declval<T&>().insert(std::declval<const value_type_t<T>&>()).second);
// a map-like type (std::map, std::unordered_map, ...) whose keys are enums; see
// JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
template<typename T, typename = void>
struct is_enum_keyed_map : std::false_type {};
template<typename T>
struct is_enum_keyed_map <
T, enable_if_t < is_detected<mapped_type_t, T>::value&&
is_detected<key_type_t, T>::value >>
{
// maps with non-unique keys (std::multimap, std::unordered_multimap, ...)
// are excluded, because an object cannot hold duplicate keys; they are
// detected by insert() returning an iterator instead of a pair<iterator, bool>
// NOLINTNEXTLINE(modernize-type-traits) we use C++11
static constexpr bool value = std::is_enum<typename T::key_type>::value &&
!(is_detected<insert_result_t, T>::value && !is_detected<insert_result_second_t, T>::value);
};
template<typename BasicJsonType, typename ConstructibleObjectType,
typename = void>
struct is_constructible_object_type_impl : std::false_type {};
+17 -1
View File
@@ -153,7 +153,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// workaround type for MSVC
using basic_json_t = NLOHMANN_BASIC_JSON_TPL;
using json_base_class_t = ::nlohmann::detail::json_base_class<CustomBaseClass>;
JSON_PRIVATE_UNLESS_TESTED:
// convenience aliases for types residing in namespace detail;
@@ -213,6 +212,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
using cbor_tag_handler_t = detail::cbor_tag_handler_t;
/// how to encode BJData
using bjdata_version_t = detail::bjdata_version_t;
/// base class used to inject custom functionality into each instance of basic_json
/// @sa https://json.nlohmann.me/api/basic_json/json_base_class_t/
using json_base_class_t = ::nlohmann::detail::json_base_class<CustomBaseClass>;
/// helper type for initializer lists of basic_json values
using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;
@@ -2690,6 +2692,20 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return *get_ptr<const binary_t*>();
}
/// @brief access the custom base class
/// @sa https://json.nlohmann.me/api/basic_json/as_base_class/
json_base_class_t& as_base_class() noexcept
{
return static_cast<json_base_class_t&>(*this);
}
/// @brief access the custom base class
/// @sa https://json.nlohmann.me/api/basic_json/as_base_class/
const json_base_class_t& as_base_class() const noexcept
{
return static_cast<const json_base_class_t&>(*this);
}
/// @}
////////////////////
-3840
View File
File diff suppressed because it is too large Load Diff
+21 -107
View File
@@ -103,10 +103,6 @@
#define JSON_STRICT_NUL_HANDLING 0
#endif
#ifndef JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
#define JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS 0
#endif
#if JSON_DIAGNOSTICS
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
#else
@@ -143,20 +139,14 @@
#define NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING
#endif
#if JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
#define NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS _ekmo
#else
#define NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS
#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, c, d, e, f, g) json_abi ## a ## b ## c ## d ## e ## f ## g
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c, d, e, f, g) \
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g)
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f) json_abi ## a ## b ## c ## d ## e ## f
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c, d, e, f) \
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f)
#define NLOHMANN_JSON_ABI_TAGS \
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
@@ -165,8 +155,7 @@
NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS, \
NLOHMANN_JSON_ABI_TAG_BRACE_INIT_COPY_SEMANTICS, \
NLOHMANN_JSON_ABI_TAG_PRECISE_STREAM_POSITION, \
NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING, \
NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS)
NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING)
// Construct the namespace version component
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
@@ -4421,30 +4410,6 @@ template<typename BasicJsonType, typename CompatibleObjectType>
struct is_compatible_object_type
: is_compatible_object_type_impl<BasicJsonType, CompatibleObjectType> {};
template<typename T>
using insert_result_t = decltype(std::declval<T&>().insert(std::declval<const value_type_t<T>&>()));
template<typename T>
using insert_result_second_t = decltype(std::declval<T&>().insert(std::declval<const value_type_t<T>&>()).second);
// a map-like type (std::map, std::unordered_map, ...) whose keys are enums; see
// JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
template<typename T, typename = void>
struct is_enum_keyed_map : std::false_type {};
template<typename T>
struct is_enum_keyed_map <
T, enable_if_t < is_detected<mapped_type_t, T>::value&&
is_detected<key_type_t, T>::value >>
{
// maps with non-unique keys (std::multimap, std::unordered_multimap, ...)
// are excluded, because an object cannot hold duplicate keys; they are
// detected by insert() returning an iterator instead of a pair<iterator, bool>
// NOLINTNEXTLINE(modernize-type-traits) we use C++11
static constexpr bool value = std::is_enum<typename T::key_type>::value &&
!(is_detected<insert_result_t, T>::value && !is_detected<insert_result_second_t, T>::value);
};
template<typename BasicJsonType, typename ConstructibleObjectType,
typename = void>
struct is_constructible_object_type_impl : std::false_type {};
@@ -6082,40 +6047,11 @@ auto from_json(BasicJsonType&& j, TupleRelated&& t)
return from_json_tuple_impl(std::forward<BasicJsonType>(j), std::forward<TupleRelated>(t), priority_tag<3> {});
}
// read a map with enum keys from an object, using the enum's own from_json for
// the keys (e.g., from NLOHMANN_JSON_SERIALIZE_ENUM); this is the form written
// with JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
template<typename BasicJsonType, typename Map>
inline bool from_json_enum_keyed_object(const BasicJsonType& j, Map& m, std::true_type /*key is enum*/)
{
if (!j.is_object())
{
return false;
}
m.clear();
for (const auto& p : *j.template get_ptr<const typename BasicJsonType::object_t*>())
{
m.emplace(BasicJsonType(p.first).template get<typename Map::key_type>(), p.second.template get<typename Map::mapped_type>());
}
return true;
}
template<typename BasicJsonType, typename Map>
inline bool from_json_enum_keyed_object(const BasicJsonType& /*j*/, Map& /*m*/, std::false_type /*key is enum*/)
{
return false;
}
template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator,
typename = enable_if_t < !std::is_constructible <
typename BasicJsonType::string_t, Key >::value >>
inline void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m)
{
// NOLINTNEXTLINE(modernize-type-traits) we use C++11
if (from_json_enum_keyed_object(j, m, std::is_enum<Key> {}))
{
return;
}
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
@@ -6136,11 +6072,6 @@ template < typename BasicJsonType, typename Key, typename Value, typename Hash,
typename BasicJsonType::string_t, Key >::value >>
inline void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m)
{
// NOLINTNEXTLINE(modernize-type-traits) we use C++11
if (from_json_enum_keyed_object(j, m, std::is_enum<Key> {}))
{
return;
}
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
@@ -6236,8 +6167,6 @@ NLOHMANN_JSON_NAMESPACE_END
#include <valarray> // valarray
#include <vector> // vector
// #include <nlohmann/detail/exceptions.hpp>
// #include <nlohmann/detail/iterators/iteration_proxy.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
@@ -6981,9 +6910,6 @@ template < typename BasicJsonType, typename CompatibleArrayType,
!is_basic_json<CompatibleArrayType>::value
#if JSON_HAS_RANGES && !defined(__MINGW32__)
&& !is_compatible_range_view<CompatibleArrayType>::value
#endif
#if JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
&& !is_enum_keyed_map<CompatibleArrayType>::value
#endif
,
int > = 0 >
@@ -7038,33 +6964,6 @@ inline void to_json(BasicJsonType& j, const CompatibleObjectType& obj)
external_constructor<value_t::object>::construct(j, obj);
}
#if JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
// store a map with enum keys as an object, using the enum's own to_json for the
// keys (e.g., from NLOHMANN_JSON_SERIALIZE_ENUM); without the macro, such maps
// are stored as arrays of [key, value] pairs
template < typename BasicJsonType, typename EnumKeyedMap,
enable_if_t < is_enum_keyed_map<EnumKeyedMap>::value&& !is_basic_json<EnumKeyedMap>::value, int > = 0 >
inline void to_json(BasicJsonType& j, const EnumKeyedMap& map)
{
typename BasicJsonType::object_t obj;
for (const auto& p : map)
{
BasicJsonType key = p.first;
if (JSON_HEDLEY_UNLIKELY(!key.is_string()))
{
JSON_THROW(type_error::create(302, concat("type must be string, but is ", key.type_name()), &key));
}
auto& key_string = *key.template get_ptr<typename BasicJsonType::string_t*>();
if (JSON_HEDLEY_UNLIKELY(!obj.emplace(key_string, BasicJsonType(p.second)).second))
{
JSON_THROW(type_error::create(318, concat("duplicate object key '", key_string, "'"), &key));
}
}
external_constructor<value_t::object>::construct(j, std::move(obj));
}
#endif
template<typename BasicJsonType>
inline void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
{
@@ -26137,7 +26036,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// workaround type for MSVC
using basic_json_t = NLOHMANN_BASIC_JSON_TPL;
using json_base_class_t = ::nlohmann::detail::json_base_class<CustomBaseClass>;
JSON_PRIVATE_UNLESS_TESTED:
// convenience aliases for types residing in namespace detail;
@@ -26197,6 +26095,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
using cbor_tag_handler_t = detail::cbor_tag_handler_t;
/// how to encode BJData
using bjdata_version_t = detail::bjdata_version_t;
/// base class used to inject custom functionality into each instance of basic_json
/// @sa https://json.nlohmann.me/api/basic_json/json_base_class_t/
using json_base_class_t = ::nlohmann::detail::json_base_class<CustomBaseClass>;
/// helper type for initializer lists of basic_json values
using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;
@@ -28674,6 +28575,20 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return *get_ptr<const binary_t*>();
}
/// @brief access the custom base class
/// @sa https://json.nlohmann.me/api/basic_json/as_base_class/
json_base_class_t& as_base_class() noexcept
{
return static_cast<json_base_class_t&>(*this);
}
/// @brief access the custom base class
/// @sa https://json.nlohmann.me/api/basic_json/as_base_class/
const json_base_class_t& as_base_class() const noexcept
{
return static_cast<const json_base_class_t&>(*this);
}
/// @}
////////////////////
@@ -32650,7 +32565,6 @@ struct formatter<nlohmann::NLOHMANN_BASIC_JSON_TPL, char> // NOLINT(cert-dcl58-c
#undef JSON_BRACE_INIT_COPY_SEMANTICS
#undef JSON_PRECISE_STREAM_POSITION
#undef JSON_STRICT_NUL_HANDLING
#undef JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
#endif
// #include <nlohmann/thirdparty/hedley/hedley_undef.hpp>
+4 -15
View File
@@ -64,10 +64,6 @@
#define JSON_STRICT_NUL_HANDLING 0
#endif
#ifndef JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
#define JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS 0
#endif
#if JSON_DIAGNOSTICS
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
#else
@@ -104,20 +100,14 @@
#define NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING
#endif
#if JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
#define NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS _ekmo
#else
#define NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS
#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, c, d, e, f, g) json_abi ## a ## b ## c ## d ## e ## f ## g
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c, d, e, f, g) \
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g)
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f) json_abi ## a ## b ## c ## d ## e ## f
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c, d, e, f) \
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f)
#define NLOHMANN_JSON_ABI_TAGS \
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
@@ -126,8 +116,7 @@
NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS, \
NLOHMANN_JSON_ABI_TAG_BRACE_INIT_COPY_SEMANTICS, \
NLOHMANN_JSON_ABI_TAG_PRECISE_STREAM_POSITION, \
NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING, \
NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS)
NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING)
// Construct the namespace version component
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
-4
View File
@@ -44,10 +44,6 @@ TEST_CASE("default namespace")
expected += "_snul";
#endif
#if JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
expected += "_ekmo";
#endif
expected += "_v" STRINGIZE(NLOHMANN_JSON_VERSION_MAJOR);
expected += "_" STRINGIZE(NLOHMANN_JSON_VERSION_MINOR);
expected += "_" STRINGIZE(NLOHMANN_JSON_VERSION_PATCH) "::basic_json";
-4
View File
@@ -45,10 +45,6 @@ TEST_CASE("default namespace without version component")
expected += "_snul";
#endif
#if JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
expected += "_ekmo";
#endif
expected += "::basic_json";
// fallback for Clang
+70
View File
@@ -9,6 +9,8 @@
#include <set>
#include <sstream>
#include <string>
#include <type_traits>
#include <utility>
#include "doctest_compatibility.h"
@@ -333,3 +335,71 @@ TEST_CASE("JSON Visit Node")
);
CHECK(expected.empty());
}
// Test accessing members of a custom base class that are hidden by members of nlohmann::basic_json
class base_class_with_hidden_members
{
public:
const char* type_name() const noexcept
{
return "custom type_name";
}
std::size_t size() const noexcept
{
return m_size;
}
std::size_t m_size = 42;
};
using json_with_hidden_base_members =
nlohmann::basic_json <
std::map,
std::vector,
std::string,
bool,
std::int64_t,
std::uint64_t,
double,
std::allocator,
nlohmann::adl_serializer,
std::vector<std::uint8_t>,
base_class_with_hidden_members
>;
TEST_CASE("JSON Node as_base_class")
{
using json = json_with_hidden_base_members;
static_assert(std::is_same<decltype(std::declval<json&>().as_base_class()), json::json_base_class_t&>::value, "");
static_assert(std::is_same<decltype(std::declval<const json&>().as_base_class()), const json::json_base_class_t&>::value, "");
static_assert(noexcept(std::declval<json&>().as_base_class()), "");
static_assert(noexcept(std::declval<const json&>().as_base_class()), "");
SECTION("non-const")
{
json j = {1, 2, 3};
CHECK(std::string(j.type_name()) == "array");
CHECK(j.size() == 3);
CHECK(std::string(j.as_base_class().type_name()) == "custom type_name");
CHECK(j.as_base_class().size() == 42);
CHECK(&j.as_base_class() == &static_cast<json::json_base_class_t&>(j));
j.as_base_class().m_size = 7;
CHECK(j.as_base_class().size() == 7);
CHECK(j.size() == 3);
}
SECTION("const")
{
const json j = {1, 2, 3};
CHECK(std::string(j.type_name()) == "array");
CHECK(j.size() == 3);
CHECK(std::string(j.as_base_class().type_name()) == "custom type_name");
CHECK(j.as_base_class().size() == 42);
CHECK(&j.as_base_class() == &static_cast<const json::json_base_class_t&>(j));
}
}
-246
View File
@@ -1,246 +0,0 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
// skip tests if JSON_DisableEnumSerialization=ON (#4384)
#if defined(JSON_DISABLE_ENUM_SERIALIZATION) && (JSON_DISABLE_ENUM_SERIALIZATION == 1)
#define SKIP_TESTS_FOR_ENUM_SERIALIZATION
#endif
// This file tests the opt-in JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS, so it defines
// the macro itself rather than relying on a -D flag, and runs in every build.
// The default behavior is tested in unit-enum_keyed_maps_default.cpp.
#ifdef JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
#undef JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
#endif
#define JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS 1
#include <nlohmann/json.hpp>
using nlohmann::json;
using nlohmann::ordered_json;
#include <cstddef>
#include <functional>
#include <map>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#define STRINGIZE_EX(x) #x
#define STRINGIZE(x) STRINGIZE_EX(x)
// NLOHMANN_JSON_SERIALIZE_ENUM uses a static std::pair
DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors")
namespace
{
// std::hash is only required for enums since C++14
struct enum_hash
{
template<typename T>
std::size_t operator()(T t) const noexcept
{
return static_cast<std::size_t>(t);
}
};
} // namespace
// the example from #4378
enum TaskState // NOLINT(cert-int09-c,readability-enum-initial-value,cppcoreguidelines-use-enum-class)
{
TS_STOPPED,
TS_RUNNING,
TS_COMPLETED,
TS_INVALID = -1,
};
// NOLINTNEXTLINE(misc-const-correctness,misc-use-internal-linkage,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - false positive
NLOHMANN_JSON_SERIALIZE_ENUM(TaskState,
{
{TS_INVALID, nullptr},
{TS_STOPPED, "stopped"},
{TS_RUNNING, "running"},
{TS_COMPLETED, "completed"},
})
enum class color {red, green, blue}; // blue is not mapped and falls back to "red"
// NOLINTNEXTLINE(misc-const-correctness,misc-use-internal-linkage,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - false positive
NLOHMANN_JSON_SERIALIZE_ENUM(color,
{
{color::red, "red"},
{color::green, "green"},
})
enum class strict_color {red, green, blue}; // blue is not mapped
// NOLINTNEXTLINE(misc-const-correctness,misc-use-internal-linkage,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - false positive
NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(strict_color,
{
{strict_color::red, "red"},
{strict_color::green, "green"},
})
enum class digit {zero, one};
// NOLINTNEXTLINE(misc-const-correctness,misc-use-internal-linkage,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - false positive
NLOHMANN_JSON_SERIALIZE_ENUM(digit,
{
{digit::zero, 0},
{digit::one, 1},
})
#ifndef SKIP_TESTS_FOR_ENUM_SERIALIZATION
enum class plain {zero, one}; // serialized as integer
#endif
TEST_CASE("JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS")
{
SECTION("the macro is part of the ABI tag")
{
const std::string ns = STRINGIZE(NLOHMANN_JSON_NAMESPACE);
CHECK(ns.find("_ekmo") != std::string::npos);
}
SECTION("std::map (#4378)")
{
using task_map = std::map<TaskState, std::string>;
const task_map m = {{TS_STOPPED, "aa"}, {TS_COMPLETED, "bb"}};
const json j = m;
CHECK(j == json::parse(R"({"stopped":"aa","completed":"bb"})"));
CHECK(j.get<task_map>() == m);
json j2;
j2["x"] = m;
CHECK(j2.dump() == R"({"x":{"completed":"bb","stopped":"aa"}})");
}
SECTION("std::map with custom comparator")
{
using task_map = std::map<TaskState, int, std::greater<TaskState>>;
const task_map m = {{TS_STOPPED, 1}, {TS_RUNNING, 2}};
const json j = m;
CHECK(j == json::parse(R"({"stopped":1,"running":2})"));
CHECK(j.get<task_map>() == m);
}
SECTION("std::unordered_map")
{
using task_map = std::unordered_map<TaskState, int, enum_hash>;
const task_map m = {{TS_STOPPED, 1}, {TS_RUNNING, 2}};
const json j = m;
CHECK(j == json::parse(R"({"stopped":1,"running":2})"));
CHECK(j.get<task_map>() == m);
}
SECTION("nested maps")
{
using nested_map = std::map<color, std::map<TaskState, int>>;
const nested_map m = {{color::green, {{TS_RUNNING, 1}}}, {color::red, {}}};
const json j = m;
CHECK(j == json::parse(R"({"green":{"running":1},"red":{}})"));
CHECK(j.get<nested_map>() == m);
}
SECTION("ordered_json keeps the order of the map")
{
using task_map = std::map<TaskState, int>;
const task_map m = {{TS_STOPPED, 1}, {TS_RUNNING, 2}, {TS_COMPLETED, 3}};
const ordered_json j = m;
CHECK(j.dump() == R"({"stopped":1,"running":2,"completed":3})");
CHECK(j.get<task_map>() == m);
}
SECTION("empty map")
{
const json j = std::map<TaskState, int>();
CHECK(j.is_object());
CHECK(j.empty());
}
SECTION("NLOHMANN_JSON_SERIALIZE_ENUM_STRICT")
{
using color_map = std::map<strict_color, int>;
const color_map m = {{strict_color::red, 1}, {strict_color::green, 2}};
const json j = m;
CHECK(j == json::parse(R"({"red":1,"green":2})"));
CHECK(j.get<color_map>() == m);
const color_map unmapped = {{strict_color::blue, 1}};
json _;
CHECK_THROWS_WITH_AS(_ = unmapped,
"[json.exception.out_of_range.410] enum value out of range for strict_color", json::out_of_range&);
}
SECTION("arrays of [key, value] pairs are still read")
{
using task_map = std::map<TaskState, int>;
const task_map m = {{TS_STOPPED, 1}};
CHECK(json::parse(R"([["stopped",1]])").get<task_map>() == m);
}
SECTION("other containers are not affected")
{
const std::vector<std::pair<TaskState, int>> pairs = {{TS_STOPPED, 1}};
const std::map<std::string, TaskState> string_keys = {{"a", TS_STOPPED}};
const std::map<int, int> int_keys = {{1, 2}};
CHECK(json(pairs) == json::parse(R"([["stopped",1]])"));
CHECK(json(string_keys) == json::parse(R"({"a":"stopped"})"));
CHECK(json(int_keys) == json::parse("[[1,2]]"));
}
SECTION("maps with non-unique keys are still stored as arrays of pairs")
{
const std::multimap<TaskState, int> mm = {{TS_STOPPED, 1}, {TS_STOPPED, 2}};
const std::unordered_multimap<TaskState, int, enum_hash> umm = {{TS_RUNNING, 3}, {TS_RUNNING, 3}};
CHECK(json(mm) == json::parse(R"([["stopped",1],["stopped",2]])"));
CHECK(json(umm) == json::parse(R"([["running",3],["running",3]])"));
}
SECTION("keys that do not serialize to strings")
{
const std::map<TaskState, int> null_key = {{TS_INVALID, 1}};
const std::map<digit, int> number_key = {{digit::zero, 1}};
json j = "unchanged";
// mapped to null
CHECK_THROWS_WITH_AS(j = null_key,
"[json.exception.type_error.302] type must be string, but is null", json::type_error&);
// mapped to a number
CHECK_THROWS_WITH_AS(j = number_key,
"[json.exception.type_error.302] type must be string, but is number", json::type_error&);
#ifndef SKIP_TESTS_FOR_ENUM_SERIALIZATION
// enum without NLOHMANN_JSON_SERIALIZE_ENUM
const std::map<plain, int> plain_key = {{plain::zero, 1}};
CHECK_THROWS_WITH_AS(j = plain_key,
"[json.exception.type_error.302] type must be string, but is number", json::type_error&);
#endif
CHECK(j == "unchanged");
}
SECTION("keys that serialize to the same string")
{
const std::map<color, int> m = {{color::red, 1}, {color::blue, 2}};
json j = "unchanged";
// color::blue is not mapped and falls back to "red"
CHECK_THROWS_WITH_AS(j = m,
"[json.exception.type_error.318] duplicate object key 'red'", json::type_error&);
CHECK(j == "unchanged");
}
}
DOCTEST_CLANG_SUPPRESS_WARNING_POP
-141
View File
@@ -1,141 +0,0 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
// This file tests maps with enum keys with the default setting of
// JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS (or whatever a -D flag sets it to).
// unit-enum_keyed_maps.cpp tests JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS=1.
// These tests are not part of unit-conversions.cpp, because that object file
// is already too big for the MinGW linker of some compilers.
#include <nlohmann/json.hpp>
using nlohmann::json;
#include <cstddef>
#include <functional>
#include <map>
#include <string>
#include <unordered_map>
// NLOHMANN_JSON_SERIALIZE_ENUM uses a static std::pair
DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors")
enum class cards {kreuz, pik, herz, karo};
// NOLINTNEXTLINE(misc-use-internal-linkage,misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - false positive
NLOHMANN_JSON_SERIALIZE_ENUM(cards,
{
{cards::kreuz, "kreuz"},
{cards::pik, "pik"},
{cards::herz, "herz"},
{cards::karo, "karo"}
})
enum TaskState // NOLINT(cert-int09-c,readability-enum-initial-value,cppcoreguidelines-use-enum-class)
{
TS_STOPPED,
TS_RUNNING,
TS_COMPLETED,
TS_INVALID = -1,
};
// NOLINTNEXTLINE(misc-const-correctness,misc-use-internal-linkage,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - false positive
NLOHMANN_JSON_SERIALIZE_ENUM(TaskState,
{
{TS_INVALID, nullptr},
{TS_STOPPED, "stopped"},
{TS_RUNNING, "running"},
{TS_COMPLETED, "completed"},
})
enum class strict_cards {kreuz, pik, herz, karo, andere}; // andere not included in mapping
// NOLINTNEXTLINE(misc-use-internal-linkage,misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - false positive
NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(strict_cards,
{
{strict_cards::kreuz, "kreuz"},
{strict_cards::pik, "pik"},
{strict_cards::herz, "herz"},
{strict_cards::karo, "karo"}
})
namespace
{
// std::hash is only required for enums since C++14
struct enum_hash
{
template<typename T>
std::size_t operator()(T t) const noexcept
{
return static_cast<std::size_t>(t);
}
};
} // namespace
// see unit-enum_keyed_maps.cpp for JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS=1
TEST_CASE("maps with enum keys")
{
using task_map = std::map<TaskState, std::string>;
using task_umap = std::unordered_map<TaskState, std::string, enum_hash>;
using task_gmap = std::map<TaskState, std::string, std::greater<TaskState>>;
using nested_map = std::map<cards, std::map<TaskState, int>>;
using strict_map = std::map<strict_cards, int>;
using int_map = std::map<int, int>;
using int_umap = std::unordered_map<int, int>;
const task_map m = {{TS_STOPPED, "aa"}, {TS_COMPLETED, "bb"}};
#if !JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
SECTION("stored as array of pairs")
{
CHECK(json(m) == json::parse(R"([["stopped","aa"],["completed","bb"]])"));
CHECK(json(task_umap {{TS_RUNNING, "cc"}}) == json::parse(R"([["running","cc"]])"));
}
#endif
SECTION("read from array of pairs")
{
CHECK(json::parse(R"([["stopped","aa"],["completed","bb"]])").get<task_map>() == m);
}
SECTION("read from object (#4378)")
{
const json j = json::parse(R"({"stopped":"aa","completed":"bb"})");
CHECK(j.get<task_map>() == m);
CHECK(j.get<task_umap>() == task_umap(m.begin(), m.end()));
CHECK(j.get<task_gmap>() == task_gmap(m.begin(), m.end()));
CHECK(json::parse(R"({"kreuz":{"stopped":1}})").get<nested_map>() == nested_map {{cards::kreuz, {{TS_STOPPED, 1}}}});
CHECK(nlohmann::ordered_json::parse(R"({"stopped":"aa","completed":"bb"})").get<task_map>() == m);
// object keys go through the enum's from_json
strict_map sm;
CHECK_THROWS_WITH_AS(json::parse(R"({"what?":1})").get_to(sm),
"[json.exception.out_of_range.410] enum value out of range for strict_cards: \"what?\"", json::out_of_range&);
}
SECTION("objects are only read for enum keys")
{
int_map im;
int_umap ium;
CHECK_THROWS_WITH_AS(json::parse(R"({"1":2})").get_to(im),
"[json.exception.type_error.302] type must be array, but is object", json::type_error&);
CHECK_THROWS_WITH_AS(json::parse(R"({"1":2})").get_to(ium),
"[json.exception.type_error.302] type must be array, but is object", json::type_error&);
}
SECTION("other types are rejected")
{
task_map tm;
CHECK_THROWS_WITH_AS(json("stopped").get_to(tm),
"[json.exception.type_error.302] type must be array, but is string", json::type_error&);
}
}
DOCTEST_CLANG_SUPPRESS_WARNING_POP
+1 -1
View File
@@ -20,7 +20,7 @@ if __name__ == '__main__':
namespaces = ['nlohmann']
abi_prefix = 'json_abi'
abi_tags = ['_diag', '_ldvcmp', '_dp', '_bics', '_psp', '_snul', '_ekmo']
abi_tags = ['_diag', '_ldvcmp', '_dp', '_bics', '_psp', '_snul']
version = '_v' + args.version.replace('.', '_')
inline_namespaces = []