Skip to content

Commit 58cfecf

Browse files
Serialize enum (#5151)
* Added NLOHNMANN_JSON_SERIALIZE_ENUM_STRICT - duplicate of NLOHMANN_JSON_SERIALIZE_ENUM Signed-off-by: Caillin Nugent <caillinn@student.unimelb.edu.au> * Added failing tests for NLOHMANN_JSON_SERIALIZE_ENUM_STRICT Signed-off-by: Caillin Nugent <caillinn@student.unimelb.edu.au> * modified NLOHMANN_JSON_SERIALIZE_STRICT to throw Signed-off-by: Caillin Nugent <caillinn@student.unimelb.edu.au> * added documentation and changed readme to include NLOHMANN_JSON_SERIALIZE_ENUM_STRICT Signed-off-by: Caillin Nugent <caillinn@student.unimelb.edu.au> * ran amalgamate Signed-off-by: Caillin Nugent <caillinn@student.unimelb.edu.au> * docs(macros): add page for JSON_SERIALIZE_ENUM_STRICT - added page to nav - added links to new page where appropriate Signed-off-by: Caillin Nugent <caillinn@student.unimelb.edu.au> * refactor(macros): make JSON_SERIALIZE_ENUM_STRICT use JSON_THROW - added templated wrapper function to fix scope error in calling JSON_THROW Signed-off-by: Caillin Nugent <caillinn@student.unimelb.edu.au> * refactor(macros): make NLOHMANN_SERIALIZE_ENUM_STRICT use error code 410 - added error code 410 to docs Signed-off-by: Caillin Nugent <caillinn@student.unimelb.edu.au> * tests(macros): add test for to_json with enum value not mentioned in mapping for NLOHMANN_JSON_SERIALIZE_ENUM_STRICT Signed-off-by: Caillin Nugent <caillinn@student.unimelb.edu.au> * Apply suggestions from code review Co-authored-by: Niels Lohmann <niels.lohmann@gmail.com> Signed-off-by: Caillin Nugent <nugentcaillin@gmail.com> * fix(macro): prevent compilation error with -Werror and -Wunused-parameter with NLOHMANN_JSON_SERIALIZE_ENUM_STRICT - casted exception to void to avoid warning Signed-off-by: Caillin Nugent <caillinn@student.unimelb.edu.au> * fix(docs): add link to NLOHMANN_SERIALIZE_ENUM_STRICT docs to exception page Signed-off-by: Caillin Nugent <caillinn@student.unimelb.edu.au> * docs(macros): add example of exception throwing for NLOHMANN_JSON_SERIALIZE_ENUM_STRICT Signed-off-by: Caillin Nugent <caillinn@student.unimelb.edu.au> * refactor(macros): add more in-depth error message to NLOHMANN_JSON_SERIALIZE_ENUM_STRICT - changed error message to follow style of #4989 - made description of throw wrapper more general - updated tests and example of exceptions Signed-off-by: Caillin Nugent <caillinn@student.unimelb.edu.au> --------- Signed-off-by: Caillin Nugent <caillinn@student.unimelb.edu.au> Signed-off-by: Caillin Nugent <nugentcaillin@gmail.com> Co-authored-by: Niels Lohmann <niels.lohmann@gmail.com>
1 parent 47202c8 commit 58cfecf

16 files changed

Lines changed: 451 additions & 2 deletions

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1105,7 +1105,7 @@ Just as in [Arbitrary Type Conversions](#arbitrary-types-conversions) above,
11051105

11061106
Other Important points:
11071107

1108-
- When using `get<ENUM_TYPE>()`, undefined JSON values will default to the first pair specified in your map. Select this default pair carefully.
1108+
- When using `get<ENUM_TYPE>()`, undefined JSON values will default to the first pair specified in your map. Select this default pair carefully. If you desire an exception in this circumstance use `NLOHMANN_JSON_SERIALIZE_ENUM_STRICT()` which behaves identically except for throwing an exception on unrecognized values.
11091109
- If an enum or JSON value is specified more than once in your map, the first matching occurrence from the top of the map will be returned when converting to or from JSON.
11101110

11111111
### Binary formats (BSON, CBOR, MessagePack, UBJSON, and BJData)

docs/mkdocs/docs/api/macros/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ header. See also the [macro overview page](../../features/macros.md).
5454
### Enums
5555

5656
- [**NLOHMANN_JSON_SERIALIZE_ENUM**](nlohmann_json_serialize_enum.md) - serialize/deserialize an enum
57+
- [**NLOHMANN_JSON_SERIALIZE_ENUM_STRICT**](nlohmann_json_serialize_enum_strict.md) - serialize/deserialize an enum with exceptions
5758

5859
### Classes and structs
5960

docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ inline void from_json(const BasicJsonType& j, type& e);
7878
## See also
7979
8080
- [Specializing enum conversion](../../features/enum_conversion.md)
81+
- [`NLOHMANN_JSON_SERIALIZE_ENUM_STRICT`](./nlohmann_json_serialize_enum_strict.md)
8182
- [`JSON_DISABLE_ENUM_SERIALIZATION`](json_disable_enum_serialization.md)
8283
8384
## Version history
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# NLOHMANN_JSON_SERIALIZE_ENUM_STRICT
2+
3+
```cpp
4+
#define NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(type, conversion...)
5+
```
6+
7+
By default, enum values are serialized to JSON as integers. In some cases, this could result in undesired behavior. If
8+
an enum is modified or re-ordered after data has been serialized to JSON, the later deserialized JSON data may be
9+
undefined or a different enum value than was originally intended.
10+
11+
`NLOHMANN_JSON_SERIALIZE_ENUM_STRICT` allows to define a user-defined serialization for every enumerator that
12+
throws an exception on undefined input.
13+
14+
## Parameters
15+
16+
`type` (in)
17+
: name of the enum to serialize/deserialize
18+
19+
`conversion` (in)
20+
: a pair of an enumerator and a JSON serialization; arbitrary pairs can be given as a comma-separated list
21+
22+
## Default definition
23+
24+
The macro adds two functions to the namespace which take care of the serialization and deserialization:
25+
26+
```cpp
27+
template<typename BasicJsonType>
28+
inline void to_json(BasicJsonType& j, const type& e);
29+
template<typename BasicJsonType>
30+
inline void from_json(const BasicJsonType& j, type& e);
31+
```
32+
33+
## Notes
34+
35+
!!! info "Prerequisites"
36+
37+
The macro must be used inside the namespace of the enum.
38+
39+
!!! important "Important notes"
40+
41+
- When using [`get<ENUM_TYPE>()`](../basic_json/get.md), undefined JSON values will throw an exception.
42+
- If an enum or JSON value is specified in multiple conversions, the first matching conversion from the top of the
43+
list will be returned when converting to or from JSON. See example 2 below.
44+
45+
## Examples
46+
47+
??? example "Example 1: Basic usage"
48+
49+
The example shows how `NLOHMANN_JSON_SERIALIZE_ENUM_STRICT` can be used to serialize/deserialize both classical enums and
50+
C++11 enum classes:
51+
52+
```cpp hl_lines="16 17 18 19 20 21 22 29 30 31 32 33"
53+
--8<-- "examples/nlohmann_json_serialize_enum_strict.cpp"
54+
```
55+
56+
Output:
57+
58+
```json
59+
--8<-- "examples/nlohmann_json_serialize_enum_strict.output"
60+
```
61+
62+
??? example "Example 2: Multiple conversions for one enumerator"
63+
64+
The example shows how to use multiple conversions for a single enumerator. In the example, `Color::red` will always
65+
be *serialized* to `"red"`, because the first occurring conversion. The second conversion, however, offers an
66+
alternative *deserialization* from `"rot"` to `Color::red`.
67+
68+
```cpp hl_lines="17"
69+
--8<-- "examples/nlohmann_json_serialize_enum_strict_2.cpp"
70+
```
71+
72+
Output:
73+
74+
```json
75+
--8<-- "examples/nlohmann_json_serialize_enum_strict_2.output"
76+
```
77+
78+
??? example "Example 3: exceptions on invalid serialization"
79+
80+
The example shows how an invalid serialization causes an exception to be thrown. In the example,
81+
Color::unknown is not defined in the mapping used to call `NLOHMANN_JSON_SERIALIZE_ENUM_STRICT`
82+
so causes an exception when used to serialize. Similarly, "what" does not refer to an enum
83+
value so also causes an exception when deserialization is attempted.
84+
85+
```cpp hl_lines="14 32 33 43 44 45"
86+
--8<-- "examples/nlohmann_json_serialize_enum_strict_err.cpp"
87+
```
88+
89+
Output:
90+
```json
91+
--8<-- "examples/nlohmann_json_serialize_enum_strict_err.output"
92+
```
93+
94+
## See also
95+
96+
- [Specializing enum conversion](../../features/enum_conversion.md)
97+
- [`NLOHMANN_JSON_SERIALIZE_ENUM`](./nlohmann_json_serialize_enum.md)
98+
- [`JSON_DISABLE_ENUM_SERIALIZATION`](json_disable_enum_serialization.md)
99+
100+
## Version history
101+
102+
Added in version 3.12.0.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#include <iostream>
2+
#include <nlohmann/json.hpp>
3+
4+
using json = nlohmann::json;
5+
6+
namespace ns
7+
{
8+
enum TaskState
9+
{
10+
TS_STOPPED,
11+
TS_RUNNING,
12+
TS_COMPLETED,
13+
TS_INVALID = -1
14+
};
15+
16+
NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(TaskState,
17+
{
18+
{ TS_INVALID, nullptr },
19+
{ TS_STOPPED, "stopped" },
20+
{ TS_RUNNING, "running" },
21+
{ TS_COMPLETED, "completed" }
22+
})
23+
24+
enum class Color
25+
{
26+
red, green, blue, unknown
27+
};
28+
29+
NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(Color,
30+
{
31+
{ Color::unknown, "unknown" }, { Color::red, "red" },
32+
{ Color::green, "green" }, { Color::blue, "blue" }
33+
})
34+
} // namespace ns
35+
36+
int main()
37+
{
38+
// serialization
39+
json j_stopped = ns::TS_STOPPED;
40+
json j_red = ns::Color::red;
41+
std::cout << "ns::TS_STOPPED -> " << j_stopped
42+
<< ", ns::Color::red -> " << j_red << std::endl;
43+
44+
// deserialization
45+
json j_running = "running";
46+
json j_blue = "blue";
47+
auto running = j_running.get<ns::TaskState>();
48+
auto blue = j_blue.get<ns::Color>();
49+
std::cout << j_running << " -> " << running
50+
<< ", " << j_blue << " -> " << static_cast<int>(blue) << std::endl;
51+
52+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ns::TS_STOPPED -> "stopped", ns::Color::red -> "red"
2+
"running" -> 1, "blue" -> 2
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#include <iostream>
2+
#include <nlohmann/json.hpp>
3+
4+
using json = nlohmann::json;
5+
6+
namespace ns
7+
{
8+
enum class Color
9+
{
10+
red, green, blue, unknown
11+
};
12+
13+
NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(Color,
14+
{
15+
{ Color::unknown, "unknown" }, { Color::red, "red" },
16+
{ Color::green, "green" }, { Color::blue, "blue" },
17+
{ Color::red, "rot" } // a second conversion for Color::red
18+
})
19+
}
20+
21+
int main()
22+
{
23+
// serialization
24+
json j_red = ns::Color::red;
25+
std::cout << static_cast<int>(ns::Color::red) << " -> " << j_red << std::endl;
26+
27+
// deserialization
28+
json j_rot = "rot";
29+
auto rot = j_rot.get<ns::Color>();
30+
auto red = j_red.get<ns::Color>();
31+
std::cout << j_rot << " -> " << static_cast<int>(rot) << std::endl;
32+
std::cout << j_red << " -> " << static_cast<int>(red) << std::endl;
33+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
0 -> "red"
2+
"rot" -> 0
3+
"red" -> 0
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#include <iostream>
2+
#include <nlohmann/json.hpp>
3+
4+
using json = nlohmann::json;
5+
6+
namespace ns
7+
{
8+
9+
enum class Color
10+
{
11+
red,
12+
green,
13+
blue,
14+
unknown // not mapped in JSON_SERIALIZE_ENUM_STRICT
15+
};
16+
17+
NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(Color,
18+
{
19+
{Color::red, "red"},
20+
{Color::green, "green"},
21+
{Color::blue, "blue"}
22+
})
23+
24+
} // namespace ns
25+
26+
27+
int main()
28+
{
29+
// invalid serialization
30+
try
31+
{
32+
// ns::color::unknown was not mapped in macro
33+
json invalid_serialization = ns::Color::unknown;
34+
}
35+
catch (const json::exception e)
36+
{
37+
std::cout << "deserialization failed: " << e.what() << std::endl;
38+
}
39+
40+
// invalid deserialization
41+
try
42+
{
43+
// what does not map to an enum
44+
json invalid_deserialization("what");
45+
ns::Color color = invalid_deserialization.get<ns::Color>();
46+
}
47+
catch (const json::exception e)
48+
{
49+
std::cout << "deserialization failed: " << e.what() << std::endl;
50+
}
51+
52+
return 0;
53+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
deserialization failed: [json.exception.out_of_range.410] enum value out of range for Color
2+
deserialization failed: [json.exception.out_of_range.410] enum value out of range for Color: "what"

0 commit comments

Comments
 (0)