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

New macros for the named JSON convertor generation #4563

Draft
wants to merge 10 commits into
base: develop
Choose a base branch
from

Conversation

radistmorse
Copy link

This patch adds several new NLOHMANN_DEFINE_<***>_WITH_NAMES macros. They behave the same way as the ones without WITH_NAMES, but require explicit JSON names. Useful for the situation when the fields in the class are following the naming convention that you do not want to expose to JSON, e.g.

    class address {
      private:
        std::string m_street;
        int m_housenumber;
        int m_postcode;

      public:
        NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_NAMES(address, "street", m_street, "housenumber", m_housenumber, "postcode", m_postcode)
    };

or if the name in JSON cannot be used as the field name because it is reserved, e.g.

    struct weird {
        std::string static_comp;
        std::string class_type;
        std::string public_field;
    };
    NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_NAMES(weird, "static", static_comp, "class", class_type, "public", public_field)

Also, this patch includes the unit tests for the new macros, and the update for the documentation.

It also fixes a small error in the docs where the DEFINE_TYPE macros were said to support up to 64 member variables, when in reality it is 63.

This is the update to the #4092 which didn't pass the amalgamation test because I had an old version of astyle. I updated the patch to include the new DERIVED variations as well as ONLY_SERIALIZE. Hopefully this one will be better.


Pull request checklist

Read the Contribution Guidelines for detailed information.

  • Changes are described in the pull request, or an existing issue is referenced.
  • The test suite compiles and runs without error.
  • Code coverage is 100%. Test cases can be added by editing the test suite.
  • The source code is amalgamated; that is, after making changes to the sources in the include/nlohmann directory, run make amalgamate to create the single-header files single_include/nlohmann/json.hpp and single_include/nlohmann/json_fwd.hpp. The whole process is described here.

@radistmorse radistmorse marked this pull request as ready for review December 20, 2024 20:26
@coveralls
Copy link

coveralls commented Dec 20, 2024

Coverage Status

coverage: 99.186%. remained the same
when pulling cd0ed45 on radistmorse:develop
into bd4fea3 on nlohmann:develop.

@nlohmann nlohmann added the please rebase Please rebase your branch to origin/develop label Jan 6, 2025
@radistmorse
Copy link
Author

I see several PRs that can potentially conflict with this one (#4528, #4560), and I'd rather not do extra work in maintaining this patch, if it's going to be pointless anyway. @nlohmann can you at least tell what are the chances of accepting this PR?

There is an unsuccessful check in the static code analysis, but first, it's a false positive, it says that the three fields are not used, when they most definitely are, and second, it's in example files, which shouldn't be statically analysed in the first place. Examples should be demonstrative and clear, not statically correct.

@nlohmann
Copy link
Owner

nlohmann commented Jan 6, 2025

I think having _WITH_NAMES macros are a good idea, but maybe it makes sense to focus on this only after #4528 and #4560 are merged.

README.md Outdated
- The `NON_INTRUSIVE` macros should be defined inside the namespace of the class/struct to create code for and thus doesn't have access to the private fields, the `INTRUSIVE` is to be defined inside the class/struct.
- The `WITH_DEFAULT` macros should be used when not all fields are required to be present in the JSON, the ones without `WITH_DEFAULTS` will raise an exception if the fields are missing.
- The `ONLY_SERIALIZE` macros should be used if you only want the `to_json` function to be created. This option excludes the `WITH_DEFAULT` variant since it is only applicable to `from_json`.
- The `WITH_NAMES` macros should be used if you want custom names for you JSON fields, the ones without `WITH_NAMES` will use the member names for JSON fields.
Copy link
Contributor

Choose a reason for hiding this comment

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

for your

README.md Outdated

In both macros, the first parameter is the name of the class/struct, and all remaining parameters name the members.
For all the macros, the first parameter is the name of the class/struct, and all remaining parameters name the members. The `DERIVED` variants require additional second argument for the parent class.
Copy link
Contributor

Choose a reason for hiding this comment

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

The DERIVED part should probably go before "all remaining"


#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1;
#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1);
#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) nlohmann_json_t.v1 = nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1);

#define NLOHMANN_JSON_TO_WITH_NAME(v1, v2) nlohmann_json_j[v1] = nlohmann_json_t.v2;
#define NLOHMANN_JSON_FROM_WITH_NAME(v1, v2) nlohmann_json_j.at(v1).get_to(nlohmann_json_t.v2);
#define NLOHMANN_JSON_FROM_WITH_DEFAULT_WITH_NAME(v1, v2) nlohmann_json_t.v2 = nlohmann_json_j.value(v1, nlohmann_json_default_obj.v2);
Copy link
Contributor

Choose a reason for hiding this comment

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

This will need to be updated with the new null handling.

@radistmorse radistmorse marked this pull request as draft January 21, 2025 13:45
@nlohmann nlohmann removed the please rebase Please rebase your branch to origin/develop label Jan 22, 2025
@@ -839,9 +839,9 @@ Some important things:

#### Simplify your life with macros

If you just want to serialize/deserialize some structs, the `to_json`/`from_json` functions can be a lot of boilerplate. There are [**several macros**](https://json.nlohmann.me/features/arbitrary_types/#simplify-your-life-with-macros) to make your life easier as long as you (1) want to use a JSON object as serialization and (2) want to use the member variable names as object keys in that object.
If you just want to serialize/deserialize some structs, the `to_json`/`from_json` functions can be a lot of boilerplate. There are [**several macros**](https://json.nlohmann.me/api/macros/#serializationdeserialization-macros) to make your life easier as long as you want to use a dedicated DTO for JSON serialization.
Copy link
Owner

Choose a reason for hiding this comment

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

What is a "dedicated DTO"?

Copy link
Author

Choose a reason for hiding this comment

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

what's a "JSON object"? :) Same thing. But I will revert the terminology. When I tried to reword it "JSON" appeared too often, so I wanted to use something else.

| <div style="color: red;">:octicons-x-circle-fill-24:</div> | <div style="color: red;">:octicons-x-circle-fill-24:</div> | <div style="color: red;">:octicons-x-circle-fill-24:</div> | [**NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE**](../api/macros/nlohmann_define_derived_type.md) |
| <div style="color: red;">:octicons-x-circle-fill-24:</div> | <div style="color: red;">:octicons-x-circle-fill-24:</div> | <div style="color: green;">:octicons-check-circle-fill-24:</div> | [**NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT**](../api/macros/nlohmann_define_derived_type.md) |
| <div style="color: red;">:octicons-x-circle-fill-24:</div> | <div style="color: green;">:octicons-check-circle-fill-24:</div> | <div style="color: grey;">:octicons-skip-fill-24:</div> | [**NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE**](../api/macros/nlohmann_define_derived_type.md) |
There are several macros to make your life easier as long as you want to use a dedicated DTO for JSON serialization. The macros are following the naming pattern, and you can chose the macro based on the needed features:
Copy link
Owner

Choose a reason for hiding this comment

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

What is a "dedicated DTO"?

@@ -33,14 +33,14 @@ For further information please refer to the corresponding macros without `WITH_N
: name of the base type (class, struct) `type` is derived from (used only in `DEFINE_DERIVED_TYPE` macros)
`json_member_name` (in)
: used in named conversion macros, must be provided for each member variable and will be used as a member variable name in the resulting json
: json name that will be used for the next member variable
Copy link
Owner

Choose a reason for hiding this comment

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

"json" -> "JSON"?

What is a "json name"?

Copy link
Author

Choose a reason for hiding this comment

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

How else would you call "a name which will be used for the corresponding value inside the JSON"?

Copy link
Contributor

@gregmarr gregmarr Jan 23, 2025

Choose a reason for hiding this comment

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

How else would you call "a name which will be used for the corresponding value inside the JSON"?

The string that will be used as the name [in the object] for the next value.

Could probably exclude the part in square brackets.

@@ -0,0 +1,73 @@
# The Named Conversion Macros
Copy link
Owner

Choose a reason for hiding this comment

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

The API pages always have the name of the function/macros as title.

| <div style="color: red;">:octicons-x-circle-fill-24:</div> | <div style="color: green;">:octicons-check-circle-fill-24:</div> | <div style="color: grey;">:octicons-skip-fill-24:</div> | [**NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE**](../api/macros/nlohmann_define_derived_type.md) |
There are several macros to make your life easier as long as you want to use a dedicated DTO for JSON serialization. The macros are following the naming pattern, and you can chose the macro based on the needed features:

- All the macros start with `NLOHMANN_DEFINE`.
Copy link
Owner

Choose a reason for hiding this comment

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

I like this explanation, but I am not sure if we should delete the table.

Copy link
Author

Choose a reason for hiding this comment

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

The table will have to be twice as long. Are you sure?

Copy link
Author

Choose a reason for hiding this comment

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

It also will have to have an additional column for names, and it is already a bit cropped on the right.


??? example
??? examples
Copy link
Owner

Choose a reason for hiding this comment

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

Leave this to "example" - example is the type of the environment. If you want to give a title, add it in quotes after:

??? example "Example with named variables"

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