Skip to content

JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS

#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).

#define JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS 0

Notes

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 maps them to strings, so a map with enum keys becomes an array of [key, value] pairs:

[["stopped", "aa"], ["completed", "bb"]]

With this macro, the same map becomes an object (see #4378):

{"completed": "bb", "stopped": "aa"}

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.

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. Data written without the macro can therefore still be read after enabling it.

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, which is stored as an integer, or an enumerator mapped to nullptr), type_error.302 is thrown. If two keys are converted to the same string (for instance, because NLOHMANN_JSON_SERIALIZE_ENUM maps an unlisted enumerator to the first entry), type_error.318 is thrown. In both cases, the target value is not changed.

Opt-in only

This macro must be defined before including <nlohmann/json.hpp>. Defining it after the include has no effect.

ABI compatibility

The value of this macro is encoded in the namespace (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

Default behavior (macro not defined)

Without the macro, a map with enum keys is stored as an array of pairs:

#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"]]
}
Objects for enum-keyed maps (macro defined to 1)

With the macro, the same map is stored as an object:

#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

Version history

  • Added in version 3.13.0 unreleased.