diff --git a/docs/docset/docSet.sql b/docs/docset/docSet.sql
index 477b2f9fe..09802e43e 100644
--- a/docs/docset/docSet.sql
+++ b/docs/docset/docSet.sql
@@ -19,6 +19,7 @@ INSERT INTO searchIndex(name, type, path) VALUES ('format_as', 'Function', 'api/
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::accept', 'Function', 'api/basic_json/accept/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::array', 'Function', 'api/basic_json/array/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::array_t', 'Type', 'api/basic_json/array_t/index.html');
+INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::as_base_class', 'Method', 'api/basic_json/as_base_class/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::at', 'Method', 'api/basic_json/at/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::back', 'Method', 'api/basic_json/back/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::basic_json', 'Constructor', 'api/basic_json/basic_json/index.html');
diff --git a/docs/mkdocs/docs/api/basic_json/as_base_class.md b/docs/mkdocs/docs/api/basic_json/as_base_class.md
new file mode 100644
index 000000000..769707140
--- /dev/null
+++ b/docs/mkdocs/docs/api/basic_json/as_base_class.md
@@ -0,0 +1,53 @@
+# nlohmann::basic_json::as_base_class
+
+```cpp
+json_base_class_t& as_base_class() noexcept;
+const json_base_class_t& as_base_class() const noexcept;
+```
+
+Returns a reference to this object as its custom base class [`json_base_class_t`](json_base_class_t.md). No copy is
+made.
+
+Since `basic_json` derives from `json_base_class_t`, a member of `basic_json` hides any member of the custom base class
+with the same name. This function makes such hidden members accessible again.
+
+## Return value
+
+reference to this object as [`json_base_class_t`](json_base_class_t.md)
+
+## Exception safety
+
+No-throw guarantee: this function never throws exceptions.
+
+## Complexity
+
+Constant.
+
+## Notes
+
+The function is equivalent to `static_cast(j)` (or `static_cast(j)`).
+
+## Examples
+
+??? example
+
+ The example shows how to use `as_base_class` to access members of the custom base class that are hidden by members
+ of `basic_json`.
+
+ ```cpp
+ --8<-- "examples/as_base_class.cpp"
+ ```
+
+ Output:
+
+ ```json
+ --8<-- "examples/as_base_class.output"
+ ```
+
+## See also
+
+- [json_base_class_t](json_base_class_t.md) - type of the custom base class
+
+## Version history
+
+- Added in version 3.13.0.
diff --git a/docs/mkdocs/docs/api/basic_json/index.md b/docs/mkdocs/docs/api/basic_json/index.md
index 866bda67a..fc934ca93 100644
--- a/docs/mkdocs/docs/api/basic_json/index.md
+++ b/docs/mkdocs/docs/api/basic_json/index.md
@@ -200,6 +200,7 @@ Direct access to the stored value of a JSON value.
- [**get_ref**](get_ref.md) - get a reference value
- [**operator ValueType**](operator_ValueType.md) - get a value
- [**get_binary**](get_binary.md) - get a binary value
+- [**as_base_class**](as_base_class.md) - access the custom base class
### Element access
diff --git a/docs/mkdocs/docs/api/basic_json/json_base_class_t.md b/docs/mkdocs/docs/api/basic_json/json_base_class_t.md
index 0d1abc9d4..6f0026558 100644
--- a/docs/mkdocs/docs/api/basic_json/json_base_class_t.md
+++ b/docs/mkdocs/docs/api/basic_json/json_base_class_t.md
@@ -27,6 +27,18 @@ A `CustomBaseClass` with non-static data members forfeits `basic_json`'s
[standard layout](https://en.cppreference.com/w/cpp/named_req/StandardLayoutType) guarantee. See
[Template Parameter Requirements](../../features/types/template_parameters.md#custombaseclass).
+#### Name conflicts
+
+Since `basic_json` derives from `CustomBaseClass`, members of `basic_json` hide members of `CustomBaseClass` with the
+same name. Hidden members remain accessible via [`as_base_class`](as_base_class.md) or by casting the value to
+`json_base_class_t`.
+
+!!! warning "Avoid generic member names"
+
+ Future versions of the library may add members to `basic_json` that hide members of `CustomBaseClass` that are
+ accessible today. To reduce the risk of such conflicts, avoid generic names for the members of `CustomBaseClass`,
+ for instance by using a distinctive prefix.
+
## Examples
??? example
@@ -45,8 +57,10 @@ A `CustomBaseClass` with non-static data members forfeits `basic_json`'s
## See also
+- [as_base_class](as_base_class.md) - access the custom base class
- [Template Parameter Requirements](../../features/types/template_parameters.md#custombaseclass) - the requirements for `CustomBaseClass`
## Version history
- Added in version 3.12.0.
+- Made a public member type in version 3.13.0; it was private before, so it could not be named outside the class.
diff --git a/docs/mkdocs/docs/examples/as_base_class.cpp b/docs/mkdocs/docs/examples/as_base_class.cpp
new file mode 100644
index 000000000..48357b045
--- /dev/null
+++ b/docs/mkdocs/docs/examples/as_base_class.cpp
@@ -0,0 +1,41 @@
+#include
+#include
+
+class base_class_with_hidden_members
+{
+ public:
+ const char* type_name() const noexcept
+ {
+ return "my_type_name";
+ }
+
+ std::size_t size() const noexcept
+ {
+ return 42;
+ }
+};
+
+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,
+ base_class_with_hidden_members
+ >;
+
+int main()
+{
+ json j = {1, 2, 3};
+
+ // the members of basic_json hide the members of the base class
+ std::cout << j.type_name() << ' ' << j.size() << '\n';
+
+ // access the hidden members of the base class
+ std::cout << j.as_base_class().type_name() << ' ' << j.as_base_class().size() << '\n';
+}
diff --git a/docs/mkdocs/docs/examples/as_base_class.output b/docs/mkdocs/docs/examples/as_base_class.output
new file mode 100644
index 000000000..5ca62a673
--- /dev/null
+++ b/docs/mkdocs/docs/examples/as_base_class.output
@@ -0,0 +1,2 @@
+array 3
+my_type_name 42
diff --git a/docs/mkdocs/mkdocs.yml b/docs/mkdocs/mkdocs.yml
index b4daf34c3..7b5512084 100644
--- a/docs/mkdocs/mkdocs.yml
+++ b/docs/mkdocs/mkdocs.yml
@@ -116,6 +116,7 @@ nav:
- 'accept': api/basic_json/accept.md
- 'array': api/basic_json/array.md
- 'array_t': api/basic_json/array_t.md
+ - 'as_base_class': api/basic_json/as_base_class.md
- 'at': api/basic_json/at.md
- 'back': api/basic_json/back.md
- 'begin': api/basic_json/begin.md
diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp
index 89ba13233..07b625a89 100644
--- a/include/nlohmann/json.hpp
+++ b/include/nlohmann/json.hpp
@@ -162,7 +162,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// workaround type for MSVC
using basic_json_t = NLOHMANN_BASIC_JSON_TPL;
- using json_base_class_t = ::nlohmann::detail::json_base_class;
JSON_PRIVATE_UNLESS_TESTED:
// convenience aliases for types residing in namespace detail;
@@ -216,6 +215,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
using cbor_tag_handler_t = detail::cbor_tag_handler_t;
/// how to encode BJData
using bjdata_version_t = detail::bjdata_version_t;
+ /// base class used to inject custom functionality into each instance of basic_json
+ /// @sa https://json.nlohmann.me/api/basic_json/json_base_class_t/
+ using json_base_class_t = ::nlohmann::detail::json_base_class;
/// helper type for initializer lists of basic_json values
using initializer_list_t = std::initializer_list>;
@@ -3023,6 +3025,20 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return *get_ptr();
}
+ /// @brief access the custom base class
+ /// @sa https://json.nlohmann.me/api/basic_json/as_base_class/
+ json_base_class_t& as_base_class() noexcept
+ {
+ return static_cast(*this);
+ }
+
+ /// @brief access the custom base class
+ /// @sa https://json.nlohmann.me/api/basic_json/as_base_class/
+ const json_base_class_t& as_base_class() const noexcept
+ {
+ return static_cast(*this);
+ }
+
/// @}
private:
diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp
index 3b74fd67f..16e87cc86 100644
--- a/single_include/nlohmann/json.hpp
+++ b/single_include/nlohmann/json.hpp
@@ -26699,7 +26699,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// workaround type for MSVC
using basic_json_t = NLOHMANN_BASIC_JSON_TPL;
- using json_base_class_t = ::nlohmann::detail::json_base_class;
JSON_PRIVATE_UNLESS_TESTED:
// convenience aliases for types residing in namespace detail;
@@ -26753,6 +26752,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
using cbor_tag_handler_t = detail::cbor_tag_handler_t;
/// how to encode BJData
using bjdata_version_t = detail::bjdata_version_t;
+ /// base class used to inject custom functionality into each instance of basic_json
+ /// @sa https://json.nlohmann.me/api/basic_json/json_base_class_t/
+ using json_base_class_t = ::nlohmann::detail::json_base_class;
/// helper type for initializer lists of basic_json values
using initializer_list_t = std::initializer_list>;
@@ -29560,6 +29562,20 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return *get_ptr();
}
+ /// @brief access the custom base class
+ /// @sa https://json.nlohmann.me/api/basic_json/as_base_class/
+ json_base_class_t& as_base_class() noexcept
+ {
+ return static_cast(*this);
+ }
+
+ /// @brief access the custom base class
+ /// @sa https://json.nlohmann.me/api/basic_json/as_base_class/
+ const json_base_class_t& as_base_class() const noexcept
+ {
+ return static_cast(*this);
+ }
+
/// @}
private:
diff --git a/tests/src/unit-custom-base-class.cpp b/tests/src/unit-custom-base-class.cpp
index a6b9b9ea4..38b665793 100644
--- a/tests/src/unit-custom-base-class.cpp
+++ b/tests/src/unit-custom-base-class.cpp
@@ -10,6 +10,8 @@
#include
#include
#include
+#include
+#include
#include
#include "doctest_compatibility.h"
@@ -406,6 +408,74 @@ TEST_CASE("JSON Visit Node")
CHECK(expected.empty());
}
+// Test accessing members of a custom base class that are hidden by members of nlohmann::basic_json
+class base_class_with_hidden_members
+{
+ public:
+ const char* type_name() const noexcept // NOLINT(readability-convert-member-functions-to-static)
+ {
+ return "custom type_name";
+ }
+
+ std::size_t size() const noexcept
+ {
+ return m_size;
+ }
+
+ std::size_t m_size = 42;
+};
+
+using json_with_hidden_base_members =
+ nlohmann::basic_json <
+ std::map,
+ std::vector,
+ std::string,
+ bool,
+ std::int64_t,
+ std::uint64_t,
+ double,
+ std::allocator,
+ nlohmann::adl_serializer,
+ std::vector,
+ base_class_with_hidden_members
+ >;
+
+TEST_CASE("JSON Node as_base_class")
+{
+ using json = json_with_hidden_base_members;
+
+ static_assert(std::is_same().as_base_class()), json::json_base_class_t&>::value, "");
+ static_assert(std::is_same().as_base_class()), const json::json_base_class_t&>::value, "");
+ static_assert(noexcept(std::declval().as_base_class()), "");
+ static_assert(noexcept(std::declval().as_base_class()), "");
+
+ SECTION("non-const")
+ {
+ json j = {1, 2, 3};
+
+ CHECK(std::string(j.type_name()) == "array");
+ CHECK(j.size() == 3);
+ CHECK(std::string(j.as_base_class().type_name()) == "custom type_name");
+ CHECK(j.as_base_class().size() == 42);
+ CHECK(&j.as_base_class() == &static_cast(j));
+
+ j.as_base_class().m_size = 7;
+ CHECK(j.as_base_class().size() == 7);
+ CHECK(j.size() == 3);
+ }
+
+ SECTION("const")
+ {
+ const json j = {1, 2, 3};
+
+ CHECK(std::string(j.type_name()) == "array");
+ CHECK(j.size() == 3);
+ CHECK(std::string(j.as_base_class().type_name()) == "custom type_name");
+ CHECK(j.as_base_class().size() == 42);
+ CHECK(&j.as_base_class() == &static_cast(j));
+ }
+}
+
// A custom base class with a const member: copy-constructible (initializing a
// const member works fine), but not copy-/move-assignable (assigning one does
// not). Used to check that copy construction never requires more than that.