From 162e13b86f1b9641c5a12b0bad8aea066df4c6a7 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 6 Oct 2026 22:33:29 +0200 Subject: [PATCH] Add with_*_t alias templates to create basic_json types with changed template parameters (#5758) * Add helper types to make it easier to create a basic_json type with modified template parameters Signed-off-by: Niels Lohmann * Rename with_changed_*_t aliases to with_*_t and merge integer/unsigned aliases Per review discussion on #3898 between gregmarr and nlohmann: - rename with_changed_X_t to with_X_t for brevity - replace the separate with_changed_integer_t/with_changed_unsigned_t aliases with a single with_integers_t - add @sa doc comment links for the upcoming documentation page Co-authored-by: Raphael Grimm <1005058+barcode@users.noreply.github.com> Signed-off-by: Niels Lohmann * Add documentation for the with_*_t member alias templates Add docs/mkdocs/docs/api/basic_json/with_t.md documenting with_object_t, with_array_t, with_string_t, with_boolean_t, with_integers_t, with_float_t, with_allocator_t, with_json_serializer_t, with_binary_t and with_base_class_t, with an accompanying example, and link the page from the basic_json member types list and the mkdocs navigation. Co-authored-by: Raphael Grimm <1005058+barcode@users.noreply.github.com> Signed-off-by: Niels Lohmann * Add tests for the with_*_t member alias templates Check with std::is_same that each with_*_t alias produces the expected basic_json type, and that with_string_t keeps nlohmann::ordered_map as the object type when used on ordered_json. Co-authored-by: Raphael Grimm <1005058+barcode@users.noreply.github.com> Signed-off-by: Niels Lohmann * Fix with_t nav entry and document chaining of the with_*_t aliases Indent the with_t entry in mkdocs.yml so it is listed under basic_json, explain that the aliases can be chained and work on ordered_json, and test both, including json::with_object_t == ordered_json. Signed-off-by: Niels Lohmann * Add docset entry for basic_json::with_t Signed-off-by: Niels Lohmann --------- Signed-off-by: Niels Lohmann Co-authored-by: barcode Co-authored-by: Raphael Grimm <1005058+barcode@users.noreply.github.com> --- docs/docset/docSet.sql | 1 + docs/mkdocs/docs/api/basic_json/index.md | 3 + docs/mkdocs/docs/api/basic_json/with_t.md | 136 ++++++++++++++++++ .../docs/examples/json_base_class_t.cpp | 14 +- docs/mkdocs/docs/examples/with_t.cpp | 18 +++ docs/mkdocs/docs/examples/with_t.output | 1 + docs/mkdocs/mkdocs.yml | 1 + include/nlohmann/json.hpp | 64 +++++++++ single_include/nlohmann/json.hpp | 64 +++++++++ tests/src/unit-allocator.cpp | 27 +--- tests/src/unit-alt-string.cpp | 11 +- tests/src/unit-custom-base-class.cpp | 29 +--- tests/src/unit-udt.cpp | 80 ++++++++++- 13 files changed, 372 insertions(+), 77 deletions(-) create mode 100644 docs/mkdocs/docs/api/basic_json/with_t.md create mode 100644 docs/mkdocs/docs/examples/with_t.cpp create mode 100644 docs/mkdocs/docs/examples/with_t.output diff --git a/docs/docset/docSet.sql b/docs/docset/docSet.sql index 8e956413c..5549b55d0 100644 --- a/docs/docset/docSet.sql +++ b/docs/docset/docSet.sql @@ -131,6 +131,7 @@ INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_string', 'Meth INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_ubjson', 'Function', 'api/basic_json/to_ubjson/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::value', 'Method', 'api/basic_json/value/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::value_t', 'Enum', 'api/basic_json/value_t/index.html'); +INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::with_t', 'Type', 'api/basic_json/with_t/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::~basic_json', 'Method', 'api/basic_json/~basic_json/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('json', 'Class', 'api/json/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer', 'Class', 'api/json_pointer/index.html'); diff --git a/docs/mkdocs/docs/api/basic_json/index.md b/docs/mkdocs/docs/api/basic_json/index.md index fc934ca93..fdb57105c 100644 --- a/docs/mkdocs/docs/api/basic_json/index.md +++ b/docs/mkdocs/docs/api/basic_json/index.md @@ -109,6 +109,9 @@ The class satisfies the following concept requirements: - **initializer_list_t** - type for initializer lists of `basic_json` values - [**input_format_t**](input_format_t.md) - type to choose the format to parse - [**json_sax_t**](../json_sax/index.md) - type for SAX events +- [**with_object_t, with_array_t, with_string_t, with_boolean_t, with_integers_t, with_float_t, with_allocator_t, + with_json_serializer_t, with_binary_t, with_base_class_t**](with_t.md) - types to create a `basic_json` type with + one (or two) replaced template parameters ### Exceptions diff --git a/docs/mkdocs/docs/api/basic_json/with_t.md b/docs/mkdocs/docs/api/basic_json/with_t.md new file mode 100644 index 000000000..2e8fee663 --- /dev/null +++ b/docs/mkdocs/docs/api/basic_json/with_t.md @@ -0,0 +1,136 @@ +# nlohmann::basic_json::with_t + +Member alias templates `with_object_t`, `with_array_t`, `with_string_t`, `with_boolean_t`, `with_integers_t`, +`with_float_t`, `with_allocator_t`, `with_json_serializer_t`, `with_binary_t`, and `with_base_class_t`. + +```cpp +template class ObjectType2> +using with_object_t = basic_json; + +template class ArrayType2> +using with_array_t = basic_json; + +template +using with_string_t = basic_json; + +template +using with_boolean_t = basic_json; + +template +using with_integers_t = basic_json; + +template +using with_float_t = basic_json; + +template class AllocatorType2> +using with_allocator_t = basic_json; + +template class JSONSerializer2> +using with_json_serializer_t = basic_json; + +template +using with_binary_t = basic_json; + +template +using with_base_class_t = basic_json; +``` + +These member alias templates make it easier to create a `basic_json` type that is identical to the current type except +for one (or, in the case of `with_integers_t`, two) of its [template parameters](index.md#template-parameters). +Spelling out all 11 template parameters of `basic_json` just to change a single one is verbose and error-prone; these +aliases only require the replacement type(s). + +with_object_t<ObjectType2> +: replaces `ObjectType` + +with_array_t<ArrayType2> +: replaces `ArrayType` + +with_string_t<StringType2> +: replaces `StringType` + +with_boolean_t<BooleanType2> +: replaces `BooleanType` + +with_integers_t<NumberIntegerType2, NumberUnsignedType2> +: replaces both `NumberIntegerType` and `NumberUnsignedType`; the two are combined into a single alias because they + are usually changed together (for instance, when switching to fixed-width integer types) + +with_float_t<NumberFloatType2> +: replaces `NumberFloatType` + +with_allocator_t<AllocatorType2> +: replaces `AllocatorType` + +with_json_serializer_t<JSONSerializer2> +: replaces `JSONSerializer` + +with_binary_t<BinaryType2> +: replaces `BinaryType` + +with_base_class_t<CustomBaseClass2> +: replaces `CustomBaseClass`; see also [`json_base_class_t`](json_base_class_t.md) + +## Notes + +All other template parameters are kept unchanged, so the resulting type still uses, for instance, the same +`ObjectType` unless `with_object_t` itself is used. + +The aliases are members of every `basic_json` specialization, including [`ordered_json`](../ordered_json.md), and the +type they produce is again a `basic_json` specialization. They can therefore be chained to replace several template +parameters at once: + +```cpp +using my_json = nlohmann::json::with_integers_t::with_float_t; +using my_ordered_json = nlohmann::ordered_json::with_string_t; +``` + +The result is the same type as spelling out all template parameters, so the order of the chained aliases does not +matter. For instance, `nlohmann::json::with_object_t` is `nlohmann::ordered_json`. + +## Examples + +??? example + + The following code shows how `with_object_t` can be used to create a JSON type that stores object elements in a + `std::map` and therefore keeps them sorted by key, unlike the default type which preserves insertion order + only when `nlohmann::ordered_json` is used. + + ```cpp + --8<-- "examples/with_t.cpp" + ``` + + Output: + + ```json + --8<-- "examples/with_t.output" + ``` + +## See also + +- [basic_json](index.md#template-parameters) - the template parameters that can be replaced +- [json_base_class_t](json_base_class_t.md) - the type used for `CustomBaseClass` + +## Version history + +- Added in version 3.13.0. diff --git a/docs/mkdocs/docs/examples/json_base_class_t.cpp b/docs/mkdocs/docs/examples/json_base_class_t.cpp index 3fb2d46a2..1dafd417f 100644 --- a/docs/mkdocs/docs/examples/json_base_class_t.cpp +++ b/docs/mkdocs/docs/examples/json_base_class_t.cpp @@ -13,19 +13,7 @@ class visitor_adaptor_with_metadata void do_visit(const Ptr& ptr, const Fnc& fnc) const; }; -using json = nlohmann::basic_json < - std::map, - std::vector, - std::string, - bool, - std::int64_t, - std::uint64_t, - double, - std::allocator, - nlohmann::adl_serializer, - std::vector, - visitor_adaptor_with_metadata - >; +using json = nlohmann::json::with_base_class_t; template void visitor_adaptor_with_metadata::visit(const Fnc& fnc) const diff --git a/docs/mkdocs/docs/examples/with_t.cpp b/docs/mkdocs/docs/examples/with_t.cpp new file mode 100644 index 000000000..665efde3e --- /dev/null +++ b/docs/mkdocs/docs/examples/with_t.cpp @@ -0,0 +1,18 @@ +#include +#include +#include + +// a JSON type that stores objects in a std::map (which keeps keys sorted) +// instead of the default ordered associative container +using sorted_json = nlohmann::json::with_object_t; + +int main() +{ + sorted_json j; + j["c"] = 1; + j["a"] = 2; + j["b"] = 3; + + // keys are sorted, because std::map is used to store the object + std::cout << j.dump() << std::endl; +} diff --git a/docs/mkdocs/docs/examples/with_t.output b/docs/mkdocs/docs/examples/with_t.output new file mode 100644 index 000000000..0886cda01 --- /dev/null +++ b/docs/mkdocs/docs/examples/with_t.output @@ -0,0 +1 @@ +{"a":2,"b":3,"c":1} diff --git a/docs/mkdocs/mkdocs.yml b/docs/mkdocs/mkdocs.yml index 5b23366bb..f38d70038 100644 --- a/docs/mkdocs/mkdocs.yml +++ b/docs/mkdocs/mkdocs.yml @@ -232,6 +232,7 @@ nav: - 'update': api/basic_json/update.md - 'value': api/basic_json/value.md - 'value_t': api/basic_json/value_t.md + - 'with_t': api/basic_json/with_t.md - byte_container_with_subtype: - 'Overview': api/byte_container_with_subtype/index.md - '(constructor)': api/byte_container_with_subtype/byte_container_with_subtype.md diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 8cf3f8397..4eeb326b8 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -226,6 +226,70 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// SAX interface type, see @ref nlohmann::json_sax using json_sax_t = json_sax; + //////////////////////////////////////////////////////////////////////////////// + // utility templates to create a json type with different template parameters // + //////////////////////////////////////////////////////////////////////////////// + + /// Json type using a different type for storing objects + /// @sa https://json.nlohmann.me/api/basic_json/with_t/ + template class ObjectType2> + using with_object_t = basic_json; + + /// Json type using a different type for storing arrays + /// @sa https://json.nlohmann.me/api/basic_json/with_t/ + template class ArrayType2> + using with_array_t = basic_json; + + /// Json type using a different type for storing strings + /// @sa https://json.nlohmann.me/api/basic_json/with_t/ + template + using with_string_t = basic_json; + + /// Json type using a different type for storing booleans + /// @sa https://json.nlohmann.me/api/basic_json/with_t/ + template + using with_boolean_t = basic_json; + + /// Json type using different types for storing signed and unsigned integers + /// @sa https://json.nlohmann.me/api/basic_json/with_t/ + template + using with_integers_t = basic_json; + + /// Json type using a different type for storing floating point numbers + /// @sa https://json.nlohmann.me/api/basic_json/with_t/ + template + using with_float_t = basic_json; + + /// Json type using a different type as base allocator + /// @sa https://json.nlohmann.me/api/basic_json/with_t/ + template class AllocatorType2> + using with_allocator_t = basic_json; + + /// Json type using a different type as json serializer + /// @sa https://json.nlohmann.me/api/basic_json/with_t/ + template class JSONSerializer2> + using with_json_serializer_t = basic_json; + + /// Json type using a different type for storing binary data + /// @sa https://json.nlohmann.me/api/basic_json/with_t/ + template + using with_binary_t = basic_json; + + /// Json type using a different type as base class + /// @sa https://json.nlohmann.me/api/basic_json/with_t/ + template + using with_base_class_t = basic_json; + //////////////// // exceptions // //////////////// diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 09e3b4f4a..be32959d5 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -27471,6 +27471,70 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// SAX interface type, see @ref nlohmann::json_sax using json_sax_t = json_sax; + //////////////////////////////////////////////////////////////////////////////// + // utility templates to create a json type with different template parameters // + //////////////////////////////////////////////////////////////////////////////// + + /// Json type using a different type for storing objects + /// @sa https://json.nlohmann.me/api/basic_json/with_t/ + template class ObjectType2> + using with_object_t = basic_json; + + /// Json type using a different type for storing arrays + /// @sa https://json.nlohmann.me/api/basic_json/with_t/ + template class ArrayType2> + using with_array_t = basic_json; + + /// Json type using a different type for storing strings + /// @sa https://json.nlohmann.me/api/basic_json/with_t/ + template + using with_string_t = basic_json; + + /// Json type using a different type for storing booleans + /// @sa https://json.nlohmann.me/api/basic_json/with_t/ + template + using with_boolean_t = basic_json; + + /// Json type using different types for storing signed and unsigned integers + /// @sa https://json.nlohmann.me/api/basic_json/with_t/ + template + using with_integers_t = basic_json; + + /// Json type using a different type for storing floating point numbers + /// @sa https://json.nlohmann.me/api/basic_json/with_t/ + template + using with_float_t = basic_json; + + /// Json type using a different type as base allocator + /// @sa https://json.nlohmann.me/api/basic_json/with_t/ + template class AllocatorType2> + using with_allocator_t = basic_json; + + /// Json type using a different type as json serializer + /// @sa https://json.nlohmann.me/api/basic_json/with_t/ + template class JSONSerializer2> + using with_json_serializer_t = basic_json; + + /// Json type using a different type for storing binary data + /// @sa https://json.nlohmann.me/api/basic_json/with_t/ + template + using with_binary_t = basic_json; + + /// Json type using a different type as base class + /// @sa https://json.nlohmann.me/api/basic_json/with_t/ + template + using with_base_class_t = basic_json; + //////////////// // exceptions // //////////////// diff --git a/tests/src/unit-allocator.cpp b/tests/src/unit-allocator.cpp index c716b3a5b..d2e23c1a7 100644 --- a/tests/src/unit-allocator.cpp +++ b/tests/src/unit-allocator.cpp @@ -48,14 +48,7 @@ TEST_CASE("bad_alloc") SECTION("bad_alloc") { // create JSON type using the throwing allocator - using bad_json = nlohmann::basic_json; + using bad_json = nlohmann::json::with_allocator_t; // creating an object should throw CHECK_THROWS_AS(bad_json(bad_json::value_t::object), std::bad_alloc&); @@ -129,14 +122,7 @@ void my_allocator_clean_up(T* p) TEST_CASE("controlled bad_alloc") { // create JSON type using the throwing allocator - using my_json = nlohmann::basic_json; + using my_json = nlohmann::json::with_allocator_t; SECTION("class json_value") { @@ -593,14 +579,7 @@ TEST_CASE("bad my_allocator::construct") { SECTION("my_allocator::construct doesn't forward") { - using bad_alloc_json = nlohmann::basic_json; + using bad_alloc_json = nlohmann::json::with_allocator_t; bad_alloc_json j; j["test"] = bad_alloc_json::array_t(); diff --git a/tests/src/unit-alt-string.cpp b/tests/src/unit-alt-string.cpp index e503a4914..961cd742b 100644 --- a/tests/src/unit-alt-string.cpp +++ b/tests/src/unit-alt-string.cpp @@ -163,16 +163,7 @@ void int_to_string(alt_string& target, std::size_t value) target = std::to_string(value).c_str(); } -using alt_json = nlohmann::basic_json < - std::map, - std::vector, - alt_string, - bool, - std::int64_t, - std::uint64_t, - double, - std::allocator, - nlohmann::adl_serializer >; +using alt_json = nlohmann::json::with_string_t; bool operator<(const char* op1, const alt_string& op2) noexcept { diff --git a/tests/src/unit-custom-base-class.cpp b/tests/src/unit-custom-base-class.cpp index b9e544705..9718d1cda 100644 --- a/tests/src/unit-custom-base-class.cpp +++ b/tests/src/unit-custom-base-class.cpp @@ -38,20 +38,7 @@ class json_metadata }; template -using json_with_metadata = - nlohmann::basic_json < - std::map, - std::vector, - std::string, - bool, - std::int64_t, - std::uint64_t, - double, - std::allocator, - nlohmann::adl_serializer, - std::vector, - json_metadata - >; +using json_with_metadata = nlohmann::json::with_base_class_t>; TEST_CASE("JSON Node Metadata") { @@ -268,19 +255,7 @@ class visitor_adaptor void do_visit(const Ptr& ptr, const Fnc& fnc) const; }; -using json_with_visitor_t = nlohmann::basic_json < - std::map, - std::vector, - std::string, - bool, - std::int64_t, - std::uint64_t, - double, - std::allocator, - nlohmann::adl_serializer, - std::vector, - visitor_adaptor - >; +using json_with_visitor_t = nlohmann::json::with_base_class_t; template void visitor_adaptor::visit(const Fnc& fnc) const diff --git a/tests/src/unit-udt.cpp b/tests/src/unit-udt.cpp index 5ad72b208..8bc04c0ba 100644 --- a/tests/src/unit-udt.cpp +++ b/tests/src/unit-udt.cpp @@ -23,6 +23,7 @@ using nlohmann::json; using namespace nlohmann::literals; // NOLINT(google-build-using-namespace) #endif +#include #include #include #include @@ -684,8 +685,7 @@ static std::ostream& operator<<(std::ostream& os, small_pod l) TEST_CASE("custom serializer for pods" * doctest::test_suite("udt")) { using custom_json = - nlohmann::basic_json; + nlohmann::json::with_json_serializer_t; auto p = udt::small_pod{42, '/', 42}; custom_json const j = p; @@ -703,7 +703,7 @@ TEST_CASE("custom serializer for pods" * doctest::test_suite("udt")) template struct another_adl_serializer; -using custom_json = nlohmann::basic_json; +using custom_json = nlohmann::json::with_json_serializer_t; template struct another_adl_serializer @@ -734,6 +734,80 @@ TEST_CASE("custom serializer that does adl by default" * doctest::test_suite("ud CHECK(me == cj.get()); } +TEST_CASE("with_*_t aliases" * doctest::test_suite("udt")) +{ + // a custom base class used to check with_base_class_t + struct custom_base_class {}; + + CHECK(std::is_same, + nlohmann::basic_json>>::value); + + CHECK(std::is_same, + nlohmann::basic_json>>::value); + + CHECK(std::is_same, + nlohmann::basic_json>>::value); + + CHECK(std::is_same, + nlohmann::basic_json>>::value); + + CHECK(std::is_same, + nlohmann::basic_json>>::value); + + CHECK(std::is_same, + nlohmann::basic_json>>::value); + + CHECK(std::is_same, + nlohmann::basic_json>>::value); + + CHECK(std::is_same, + nlohmann::basic_json>>::value); + + CHECK(std::is_same>, + nlohmann::basic_json>>::value); + + CHECK(std::is_same, + nlohmann::basic_json, custom_base_class>>::value); + + // with_string_t on ordered_json must keep ordered_map as the object type + CHECK(std::is_same, + nlohmann::basic_json>>::value); + + // the aliases are members of the resulting type, so they can be chained + CHECK(std::is_same::with_float_t, + nlohmann::basic_json>>::value); + CHECK(std::is_same::with_integers_t, + json::with_integers_t::with_float_t>::value); + + // replacing the object type of json with ordered_map yields ordered_json + CHECK(std::is_same, nlohmann::ordered_json>::value); + CHECK(std::is_same, json>::value); +} + TEST_CASE("different basic_json types conversions") { SECTION("null")