Skip to content
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

add strict enum de/serialization macro #4612

Open
wants to merge 13 commits into
base: develop
Choose a base branch
from

Conversation

hnampally
Copy link
Contributor

@hnampally hnampally commented Jan 20, 2025

#3992

  • The changes are described in detail, both the what and why.
  • If applicable, an #3992 is referenced.
  • The Code coverage remained at 100%. A test case for every new line of code.
  • If applicable, the documentation is updated.
  • The source code is amalgamated by running make amalgamate.

Read the Contribution Guidelines for detailed information.

Copy link

🔴 Amalgamation check failed! 🔴

The source code has not been amalgamated. @hnampally
Please read and follow the Contribution Guidelines.

@coveralls
Copy link

coveralls commented Jan 20, 2025

Coverage Status

coverage: 99.186%. remained the same
when pulling 6747555 on hnampally:issue-3992
into 606b634 on nlohmann:develop.

Signed-off-by: Harinath Nampally <[email protected]>
Signed-off-by: Harinath Nampally <[email protected]>
Signed-off-by: Harinath Nampally <[email protected]>
Signed-off-by: Harinath Nampally <[email protected]>
Signed-off-by: Harinath Nampally <[email protected]>
Signed-off-by: Harinath Nampally <[email protected]>
Copy link

🔴 Amalgamation check failed! 🔴

The source code has not been amalgamated. @hnampally
Please read and follow the Contribution Guidelines.

Signed-off-by: Harinath Nampally <[email protected]>
Signed-off-by: Harinath Nampally <[email protected]>
Signed-off-by: Harinath Nampally <[email protected]>
template<typename T>
[[noreturn]] inline void json_throw_from_serialize_macro(T&& exception)
{
#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) || defined(EXCEPTIONS)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When defining JSON_THROW in the same file, we use the following code to detect exceptions. Please use the same here to avoid issues.

#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh okay, thanks!

}); \
if (it == std::end(m)) { \
auto value = static_cast<typename std::underlying_type<ENUM_TYPE>::type>(e); \
nlohmann::detail::json_throw_from_serialize_macro(nlohmann::detail::type_error::create(302, nlohmann::detail::concat("can't serialize - enum value ", std::to_string(value), " out of range"), &j)); \
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like the exception message. What about

serialization failed: enum value ... is out of range

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes that sounds better, thanks!

return ej_pair.second == j; \
}); \
if (it == std::end(m)) \
nlohmann::detail::json_throw_from_serialize_macro(nlohmann::detail::type_error::create(302, nlohmann::detail::concat("can't deserialize - invalid json value : ", j.dump()), &j)); \
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above:

deserialization failed: invalid JSON value '...'

Signed-off-by: Harinath Nampally <[email protected]>
#define NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(type, conversion...)
```

The `NLOHMANN_JSON_SERIALIZE_ENUM_STRICT` allows to define a user-defined serialization for every enumerator.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is missing some words.

The ... macro allows defining a ...
or
The ... macro allows one to define a ...

I can see this is basically the same text as in the original, it should probably be updated there too.

: name of the enum to serialize/deserialize

`conversion` (in)
: a pair of an enumerator and a JSON serialization; arbitrary pairs can be given as a comma-separated list
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say "pairs" rather than "a pair", and "a JSON serialization" is odd, maybe "the string to be used in the JSON serialization. The "arbitrary pairs can be given as a comma-separated list" sounds weird to me.
Maybe

A list of parameters alternating between an enumerator value and a string to use in the JSON serialization.

!!! important "Important notes"

- If an enum value appears more than once in the mapping, only the first occurrence will be used for serialization,
subsequent mappings for the same enum value will be ignored.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be worth putting in something here like "will currently be ignored but may result in an error in the future"?


- If an enum value appears more than once in the mapping, only the first occurrence will be used for serialization,
subsequent mappings for the same enum value will be ignored.
- If a JSON value appears more than once in the mapping, only the first occurrence will be used for deserialization,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"If a string value" since it's not an arbitrary json value but a string....

Oh, OH, it doesn't require that it be a string, does it?

😮

There's nothing in the code that requires that this be a string.

NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(Color, {
  { Red, "red" },
  { Green, nlohmann::json(1) },
  { Blue, nlohmann::json({{{"purple", "people eater"}}})}
})


int main()
{
  nlohmann::json j;
  j = { Red, Green, Blue };
  std::cout << j.dump() << "\n";
  auto j2 = nlohmann::json::parse(j.dump());
  std::vector<Color> vec = j2;
  std::cout << vec[0] << vec[1] << vec[2];
}
["red",1,[{"purple":"people eater"}]]
012

@hnampally Is this intended behavior, or did this just accidentally fall out of the second parameter being BasicJsonType instead of std::string?

If this is intended behavior, it should definitely be documented before someone trips upon it accidentally, or someone relies on it and then someone else "fixes" it.

If it's not intended behavior, then it should be fixed.

Copy link
Contributor Author

@hnampally hnampally Jan 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gregmarr thanks for bringing this up but I am not really sure about the requirements for this feature but I am open for either, @nlohmann please weigh in!

Signed-off-by: Harinath Nampally <[email protected]>
Expected output:

```
[json.exception.type_error.302] serialization failed: enum value 3 is out of range
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also use an include here:

--8<-- "examples/nlohmann_json_serialize_enum_strict.output"

Expected output:

```
[json.exception.type_error.302] deserialization failed: invalid JSON value "yellow"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also use an include here:

--8<-- "examples/nlohmann_json_deserialize_enum_strict.output"


int main()
{

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the empty line.

Also: move line

json j_yellow = "yellow";

above the comment

// deserialization

@gregmarr
Copy link
Contributor

It looks like you have a space in your filename here:
docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum _strict.md

Also, the discussion about strings vs arbitrary json for the value has been marked outdated because there was a change in the file. @nlohmann did you see that discussion?

Signed-off-by: Harinath Nampally <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants