-
-
Notifications
You must be signed in to change notification settings - Fork 6.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
enum to json mapping #1208
Comments
Another option for the macro is to use the std::find_if() algorithm. This a little more wordy but would do away with the need for the multi_bimap class: #define NLOHMANN_JSON_SERIALIZE_ENUM( ENUM_TYPE, ...)\
inline void to_json( nlohmann::json& j, const ENUM_TYPE& e) {\
static const std::vector<std::pair<ENUM_TYPE,nlohmann::json>> m=__VA_ARGS__;\
auto it = std::find_if(m.cbegin(),m.cend(),[e](const auto& ej_pair)->bool{return ej_pair.first==e;});\
j = ((it!=m.cend())?it:m.cbegin())->second;\
}\
inline void from_json( const nlohmann::json& j, ENUM_TYPE& e) {\
static const std::vector<std::pair<ENUM_TYPE,nlohmann::json>> m=__VA_ARGS__;\
auto it = std::find_if(m.cbegin(),m.cend(),[j](const auto& ej_pair)->bool{return ej_pair.second==j;});\
e = ((it!=m.cend())?it:m.cbegin())->first;\
} |
I like the proposal from @dgendreau. Unless anyone objects, I would add it for the 3.2.1 release. @theodelrieu - What do you think? |
It looks good to me as well. I just have two remarks:
Other than that, it's all good! |
Thanks for the quick feedback! |
You might also want to have a second version of the macro that takes (ENUM_TYPE, JSON_TYPE, ...) to allow the user to map to something other than the default nlohmann::json type. |
@dgendreau this can already be done in the macro, by templating on |
I hadnt thought of that. Yes, thats a cleaner approach. |
Thanks for the feedback @theodelrieu. #define NLOHMANN_JSON_SERIALIZE_ENUM( ENUM_TYPE, ...)\
template<typename BasicJsonType>\
inline void to_json( BasicJsonType& j, ENUM_TYPE e) {\
static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE " must be an enum!");\
static const std::vector<std::pair<ENUM_TYPE,BasicJsonType>> m=__VA_ARGS__;\
auto it = std::find_if(m.cbegin(),m.cend(),[e](const std::pair<ENUM_TYPE,BasicJsonType>& ej_pair)->bool{return ej_pair.first==e;});\
j = ((it!=m.cend())?it:m.cbegin())->second;\
}\
template<typename BasicJsonType>\
inline void from_json( const BasicJsonType& j, ENUM_TYPE& e) {\
static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE " must be an enum!");\
static const std::vector<std::pair<ENUM_TYPE,BasicJsonType>> m=__VA_ARGS__;\
auto it = std::find_if(m.cbegin(),m.cend(),[j](const std::pair<ENUM_TYPE,BasicJsonType>& ej_pair)->bool{return ej_pair.second==j;});\
e = ((it!=m.cend())?it:m.cbegin())->first;\
} |
No problem! That's it, but there's one |
Suggested user documentation for this feature: Specializing enum conversionBy default, enum values are serialized to JSON as integers. In some cases this could result in undesired behavior. If an enum is modified or re-ordered after data has been serialized to JSON, the later de-serialized JSON data may be undefined or a different enum value than was originally intended. It is possible to more precisely specify how a given enum is mapped to and from JSON as shown below: // example enum type declaration
enum TaskState {
TS_STOPPED,
TS_RUNNING,
TS_COMPLETED,
TS_INVALID=-1,
};
// map TaskState values to JSON as strings
NLOHMANN_JSON_SERIALIZE_ENUM( TaskState, {
{TS_INVALID, nullptr},
{TS_STOPPED, "stopped"},
{TS_RUNNING, "running"},
{TS_COMPLETED, "completed"},
}); The Usage: // enum to JSON as string
json j = TS_STOPPED;
assert(j == "stopped");
// json string to enum
json j3 = "running";
assert( j3.get<TaskState>() == TS_RUNNING);
// undefined json value to enum (where the first map entry above is the default)
json jPi = 3.14;
assert( jPi.get<TaskState>() == TS_INVALID ); Just as in Arbitrary Type Conversions above,
Other Important points:
|
Should we look into serializing third party enums with an adl_serializer<> specialization or is that overkill? I think that would require the to_json()/from_json() functions to also be declared static. |
The library must not specialize
About 4, it's the only place where we can put types that are in a different namespace, because each overload is visible by That's why specializations are reserved to users only. Note: everything said here applies to |
I'm not sure about the error handling here, but we cannot please everybody anyway... One last thing that would be good: adding a |
Re: adl_serializer, Re: error handling, Re: static_assert, |
Could you add the
I misunderstood what you meant, it would be possible of course, but I'd rather leave specializations being explicit to write. For now at least. |
Done |
Looks good to me, time to open a PR! :) |
ok. ill do that after work today. |
oh, what file would you recommend adding the macro to? |
Good question... Either in |
@dgendreau Can you open a PR? |
Hi Niels, |
For amalgamation, you do not need CMake, but only Python and astyle. |
If you are still stuck on this, you can also send me a patch and I can create a PR. |
@dgendreau Can you send me a patch so I can make a PR? |
@dgendreau Could you open a PR or send me a patch? |
@dgendreau @theodelrieu Do we agree that #1208 (comment) should be added to the library? If so, I could create a PR. |
Hi. Sorry I was away. I'll post a PR this evening. |
@dgendreau Any news on this? As I wrote above: I could also create a PR if we agree that #1208 (comment) should be the fix. |
PR #1323 contains the macro definition, the documentation, and a few test cases. Thanks everybody for the good work! As I had some time, I opened the PR myself. It would be great if you could have a look whether anything else needs to be added. |
One use-case I run into a lot is mapping enums to serialize as non-integers such as strings. I know its not really modern C++ to use preprocessor macros, but I wrote one that declares a from_json()/to_json() specialization in one statement to make it easier and less redundant to specialize the mapping between a given set of enum values and a set of json values. Motivating example:
The JSON_SERIALIZE_ENUM() macro above is implemented as:
This example is still just a sketch since it does not take into account that basic_json can be specialized for different types.
One benefit of the multi_bimap<> is that it allows multiple enum values to map to the same JSON value and multiple JSON values to map to the same enum. The first matching pair found in at_right()/at_left is returned.
I can provide you with implementation details for the multi_bimap<TL,TR> class but it is mostly just a simple wrapper around a vector<pair<TL,TR>> that is linear searched in at_right()/at_left(). When a key is not found in at_right() or at_left(), the first pair is used as default values.
The text was updated successfully, but these errors were encountered: