// __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) // | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // // SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // 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 using nlohmann::json; #include #include #include #include #include // 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 std::size_t operator()(T t) const noexcept { return static_cast(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; using task_umap = std::unordered_map; using task_gmap = std::map>; using nested_map = std::map>; using strict_map = std::map; using int_map = std::map; using int_umap = std::unordered_map; 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() == m); } SECTION("read from object (#4378)") { const json j = json::parse(R"({"stopped":"aa","completed":"bb"})"); CHECK(j.get() == m); CHECK(j.get() == task_umap(m.begin(), m.end())); CHECK(j.get() == task_gmap(m.begin(), m.end())); CHECK(json::parse(R"({"kreuz":{"stopped":1}})").get() == nested_map {{cards::kreuz, {{TS_STOPPED, 1}}}}); CHECK(nlohmann::ordered_json::parse(R"({"stopped":"aa","completed":"bb"})").get() == 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") { // built rather than parsed, so that the messages do not gain a byte // range with JSON_DIAGNOSTIC_POSITIONS const json j = {{"1", 2}}; int_map im; int_umap ium; CHECK_THROWS_WITH_AS(j.get_to(im), "[json.exception.type_error.302] type must be array, but is object", json::type_error&); CHECK_THROWS_WITH_AS(j.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