Allow optionally (de)serializing private members
This commit is contained in:
parent
e29dcce40f
commit
0d74d915f8
35
README.md
35
README.md
|
@ -139,6 +139,7 @@ So beside the `BOOST_HANA_DEFINE_STRUCT` macro, the usage remains the same.
|
||||||
* No context information for errors like type-mismatch available
|
* No context information for errors like type-mismatch available
|
||||||
* Inherited members not considered
|
* Inherited members not considered
|
||||||
* Support for enums is unlikely
|
* Support for enums is unlikely
|
||||||
|
* Attempt to access private members can not be prevented
|
||||||
|
|
||||||
### Enable reflection for 3rd party classes/structs
|
### Enable reflection for 3rd party classes/structs
|
||||||
It is obvious that the previously shown examples do not work for classes
|
It is obvious that the previously shown examples do not work for classes
|
||||||
|
@ -162,12 +163,34 @@ ReflectiveRapidJSON::JsonReflector::toJson(...).GetString();
|
||||||
ReflectiveRapidJSON::JsonReflector::fromJson<ThridPartyStruct>("...");
|
ReflectiveRapidJSON::JsonReflector::fromJson<ThridPartyStruct>("...");
|
||||||
```
|
```
|
||||||
|
|
||||||
The code generator will emit the same code in the same way as `JsonSerializable` was
|
The code generator will emit the code in the same way as if `JsonSerializable` was
|
||||||
used.
|
used.
|
||||||
|
|
||||||
### Further examples
|
By the way, the functions in the `ReflectiveRapidJSON::JsonReflector` namespace can also
|
||||||
Checkout the test cases for further examples. Relevant files are in
|
be used when inheriting from `JsonSerializable` (instead of the member functions).
|
||||||
the directories `lib/tests` and `generator/tests`.
|
|
||||||
|
### (De)serializing private members
|
||||||
|
By default, private members are not considered for (de)serialization. However, it is possible
|
||||||
|
to enable this by adding `friend` methods for the helper functions of Reflective RapidJSON.
|
||||||
|
|
||||||
|
To make things easier, there's a macro provided:
|
||||||
|
```
|
||||||
|
struct SomeStruct : public JsonSerializable<SomeStruct> {
|
||||||
|
REFLECTIVE_RAPIDJSON_ENABLE_PRIVATE_MEMBERS(SomeStruct);
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::string publicMember = "will be (de)serialized anyways";
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string privateMember = "will be (de)serialized with the help of REFLECTIVE_RAPIDJSON_ENABLE_PRIVATE_MEMBERS macro";
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Caveats
|
||||||
|
* It will obviously not work for 3rd party structs.
|
||||||
|
* This way to allow (de)serialization of private members must be applied when using Boost.Hana
|
||||||
|
and there are any private members present. The reason is that accessing the private members can
|
||||||
|
currently not prevented when using Boost.Hana.
|
||||||
|
|
||||||
### Custom (de)serialization
|
### Custom (de)serialization
|
||||||
Sometimes it is appropriate to implement custom (de)serialization. For instance, a
|
Sometimes it is appropriate to implement custom (de)serialization. For instance, a
|
||||||
|
@ -178,6 +201,10 @@ An example for such custom (de)serialization can be found in the file
|
||||||
`json/reflector-chronoutilities.h`. It provides (de)serialization of `DateTime` and
|
`json/reflector-chronoutilities.h`. It provides (de)serialization of `DateTime` and
|
||||||
`TimeSpan` objects from the C++ utilities library.
|
`TimeSpan` objects from the C++ utilities library.
|
||||||
|
|
||||||
|
### Further examples
|
||||||
|
Checkout the test cases for further examples. Relevant files are in
|
||||||
|
the directories `lib/tests` and `generator/tests`.
|
||||||
|
|
||||||
## Install instructions
|
## Install instructions
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "../lib/json/serializable.h"
|
#include "../lib/json/serializable.h"
|
||||||
|
|
||||||
#include <clang/AST/DeclCXX.h>
|
#include <clang/AST/DeclCXX.h>
|
||||||
|
#include <clang/AST/DeclFriend.h>
|
||||||
#include <clang/AST/DeclTemplate.h>
|
#include <clang/AST/DeclTemplate.h>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
@ -43,7 +44,7 @@ void JsonSerializationCodeGenerator::addDeclaration(clang::Decl *decl)
|
||||||
// check for template specializations to adapt a 3rd party class/struct
|
// check for template specializations to adapt a 3rd party class/struct
|
||||||
if (decl->getKind() == clang::Decl::Kind::ClassTemplateSpecialization) {
|
if (decl->getKind() == clang::Decl::Kind::ClassTemplateSpecialization) {
|
||||||
auto *const templRecord = static_cast<clang::ClassTemplateSpecializationDecl *>(decl);
|
auto *const templRecord = static_cast<clang::ClassTemplateSpecializationDecl *>(decl);
|
||||||
if (templRecord->getQualifiedNameAsString() == JsonReflector::AdaptedJsonSerializable<void>::qualifiedName) {
|
if (templRecord->getQualifiedNameAsString() == AdaptedJsonSerializable<void>::qualifiedName) {
|
||||||
const clang::TemplateArgumentList &templateArgs = templRecord->getTemplateArgs();
|
const clang::TemplateArgumentList &templateArgs = templRecord->getTemplateArgs();
|
||||||
if (templateArgs.size() != 1 || templateArgs.get(0).getKind() != clang::TemplateArgument::Type) {
|
if (templateArgs.size() != 1 || templateArgs.get(0).getKind() != clang::TemplateArgument::Type) {
|
||||||
return; // FIXME: use Clang diagnostics to print warning
|
return; // FIXME: use Clang diagnostics to print warning
|
||||||
|
@ -59,7 +60,7 @@ void JsonSerializationCodeGenerator::addDeclaration(clang::Decl *decl)
|
||||||
|
|
||||||
// add any other records
|
// add any other records
|
||||||
m_records.emplace_back(record);
|
m_records.emplace_back(record);
|
||||||
}
|
} break;
|
||||||
case clang::Decl::Kind::Enum:
|
case clang::Decl::Kind::Enum:
|
||||||
// TODO: add enums
|
// TODO: add enums
|
||||||
break;
|
break;
|
||||||
|
@ -88,6 +89,27 @@ void JsonSerializationCodeGenerator::generate(ostream &os) const
|
||||||
// add push and pull functions for each class, for an example of the resulting
|
// add push and pull functions for each class, for an example of the resulting
|
||||||
// output, see ../lib/tests/jsonserializable.cpp (code under comment "pretend serialization code...")
|
// output, see ../lib/tests/jsonserializable.cpp (code under comment "pretend serialization code...")
|
||||||
for (const RelevantClass &relevantClass : relevantClasses) {
|
for (const RelevantClass &relevantClass : relevantClasses) {
|
||||||
|
bool pushPrivateMembers = false;
|
||||||
|
bool pullPrivateMembers = false;
|
||||||
|
for (const clang::FriendDecl *const friendDecl : relevantClass.record->friends()) {
|
||||||
|
const clang::NamedDecl *const actualFriendDecl = friendDecl->getFriendDecl();
|
||||||
|
if (!actualFriendDecl /* && decl->getKind() != clang::Decl::Kind::FunctionTemplate */) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const string friendName(actualFriendDecl->getQualifiedNameAsString());
|
||||||
|
if (friendName == "ReflectiveRapidJSON::JsonReflector::push") {
|
||||||
|
pushPrivateMembers = true;
|
||||||
|
}
|
||||||
|
if (friendName == "ReflectiveRapidJSON::JsonReflector::pull") {
|
||||||
|
pullPrivateMembers = true;
|
||||||
|
}
|
||||||
|
cout << "friend-kind: " << actualFriendDecl->getDeclKindName() << endl;
|
||||||
|
cout << "friend: " << actualFriendDecl->getQualifiedNameAsString() << endl;
|
||||||
|
if (pushPrivateMembers && pullPrivateMembers) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// write comment
|
// write comment
|
||||||
os << "// define code for (de)serializing " << relevantClass.qualifiedName << " objects\n";
|
os << "// define code for (de)serializing " << relevantClass.qualifiedName << " objects\n";
|
||||||
|
|
||||||
|
@ -103,7 +125,7 @@ void JsonSerializationCodeGenerator::generate(ostream &os) const
|
||||||
}
|
}
|
||||||
os << " // push members\n";
|
os << " // push members\n";
|
||||||
for (const clang::FieldDecl *field : relevantClass.record->fields()) {
|
for (const clang::FieldDecl *field : relevantClass.record->fields()) {
|
||||||
if (field->getAccess() == clang::AS_public) {
|
if (pushPrivateMembers || field->getAccess() == clang::AS_public) {
|
||||||
os << " push(reflectable." << field->getName() << ", \"" << field->getName() << "\", value, allocator);\n";
|
os << " push(reflectable." << field->getName() << ", \"" << field->getName() << "\", value, allocator);\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,7 +150,7 @@ void JsonSerializationCodeGenerator::generate(ostream &os) const
|
||||||
" }\n"
|
" }\n"
|
||||||
" // pull members\n";
|
" // pull members\n";
|
||||||
for (const clang::FieldDecl *field : relevantClass.record->fields()) {
|
for (const clang::FieldDecl *field : relevantClass.record->fields()) {
|
||||||
if (field->getAccess() == clang::AS_public) {
|
if (pullPrivateMembers || field->getAccess() == clang::AS_public) {
|
||||||
os << " pull(reflectable." << field->getName() << ", \"" << field->getName() << "\", value, errors);\n";
|
os << " pull(reflectable." << field->getName() << ", \"" << field->getName() << "\", value, errors);\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -220,12 +220,11 @@ void JsonGeneratorTests::testCustomSerialization()
|
||||||
*/
|
*/
|
||||||
void JsonGeneratorTests::test3rdPartyAdaption()
|
void JsonGeneratorTests::test3rdPartyAdaption()
|
||||||
{
|
{
|
||||||
static_assert(ReflectiveRapidJSON::JsonReflector::AdaptedJsonSerializable<NotJsonSerializable>::value,
|
static_assert(
|
||||||
"can serialize NotJsonSerializable because of adaption macro");
|
ReflectiveRapidJSON::AdaptedJsonSerializable<NotJsonSerializable>::value, "can serialize NotJsonSerializable because of adaption macro");
|
||||||
static_assert(!ReflectiveRapidJSON::JsonReflector::AdaptedJsonSerializable<OtherNotJsonSerializable>::value,
|
static_assert(!ReflectiveRapidJSON::AdaptedJsonSerializable<OtherNotJsonSerializable>::value,
|
||||||
"can not serialize OtherNotJsonSerializable because adaption macro missing");
|
"can not serialize OtherNotJsonSerializable because adaption macro missing");
|
||||||
static_assert(!ReflectiveRapidJSON::JsonReflector::AdaptedJsonSerializable<ReallyNotJsonSerializable>::value,
|
static_assert(!ReflectiveRapidJSON::AdaptedJsonSerializable<ReallyNotJsonSerializable>::value, "can not serialize ReallyNotJsonSerializable");
|
||||||
"can not serialize ReallyNotJsonSerializable");
|
|
||||||
|
|
||||||
const NotJsonSerializable test;
|
const NotJsonSerializable test;
|
||||||
const string str("{\"butSerializableAnyways\":\"useful to adapt 3rd party structs\"}");
|
const string str("{\"butSerializableAnyways\":\"useful to adapt 3rd party structs\"}");
|
||||||
|
|
|
@ -28,12 +28,19 @@ private:
|
||||||
string privateString = "not going to be serialized";
|
string privateString = "not going to be serialized";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class JsonGeneratorTests;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief The NestedTestStruct struct inherits from JsonSerializable and should hence have functional fromJson()
|
* \brief The NestedTestStruct struct inherits from JsonSerializable and should hence have functional fromJson()
|
||||||
* and toJson() methods. This is asserted in JsonGeneratorTests::testNesting();
|
* and toJson() methods. This is asserted in JsonGeneratorTests::testNesting();
|
||||||
*/
|
*/
|
||||||
struct NestedTestStruct : public JsonSerializable<NestedTestStruct> {
|
struct NestedTestStruct : public JsonSerializable<NestedTestStruct> {
|
||||||
|
REFLECTIVE_RAPIDJSON_ENABLE_PRIVATE_MEMBERS(NestedTestStruct);
|
||||||
|
friend class JsonGeneratorTests;
|
||||||
|
|
||||||
list<vector<TestStruct>> nested;
|
list<vector<TestStruct>> nested;
|
||||||
|
|
||||||
|
private:
|
||||||
deque<double> deq;
|
deque<double> deq;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -24,26 +24,13 @@
|
||||||
namespace ReflectiveRapidJSON {
|
namespace ReflectiveRapidJSON {
|
||||||
|
|
||||||
template <typename Type> struct JsonSerializable;
|
template <typename Type> struct JsonSerializable;
|
||||||
|
template <typename Type> struct AdaptedJsonSerializable;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief The JsonReflector namespace contains helper functions to ease the use of RapidJSON for automatic (de)serialization.
|
* \brief The JsonReflector namespace contains helper functions to ease the use of RapidJSON for automatic (de)serialization.
|
||||||
*/
|
*/
|
||||||
namespace JsonReflector {
|
namespace JsonReflector {
|
||||||
|
|
||||||
template <typename T> struct AdaptedJsonSerializable : Traits::Bool<false> {
|
|
||||||
static constexpr const char *name = "AdaptedJsonSerializable";
|
|
||||||
static constexpr const char *qualifiedName = "ReflectiveRapidJSON::JsonReflector::AdaptedJsonSerializable";
|
|
||||||
};
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \def The REFLECTIVE_RAPIDJSON_MAKE_JSON_SERIALIZABLE macro allows to adapt (de)serialization for types defined in 3rd party header files.
|
|
||||||
* \remarks The struct will not have the toJson() and fromJson() methods available. Use the corresponding functions in the namespace
|
|
||||||
* ReflectiveRapidJSON::JsonReflector instead.
|
|
||||||
*/
|
|
||||||
#define REFLECTIVE_RAPIDJSON_MAKE_JSON_SERIALIZABLE(T) \
|
|
||||||
template <> struct ::ReflectiveRapidJSON::JsonReflector::AdaptedJsonSerializable<T> : Traits::Bool<true> { \
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Casts the specified \a size to the size type used by RapidJSON ensuring no overflow happens.
|
* \brief Casts the specified \a size to the size type used by RapidJSON ensuring no overflow happens.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -98,6 +98,48 @@ const JsonSerializable<Type> &as(const Type &serializable)
|
||||||
return static_cast<const JsonSerializable<Type> &>(serializable);
|
return static_cast<const JsonSerializable<Type> &>(serializable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief The AdaptedJsonSerializable class allows considering 3rd party classes as serializable.
|
||||||
|
*/
|
||||||
|
template <typename T> struct AdaptedJsonSerializable : Traits::Bool<false> {
|
||||||
|
static constexpr const char *name = "AdaptedJsonSerializable";
|
||||||
|
static constexpr const char *qualifiedName = "ReflectiveRapidJSON::AdaptedJsonSerializable";
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \def The REFLECTIVE_RAPIDJSON_MAKE_JSON_SERIALIZABLE macro allows to adapt (de)serialization for types defined in 3rd party header files.
|
||||||
|
* \remarks The struct will not have the toJson() and fromJson() methods available. Use the corresponding functions in the namespace
|
||||||
|
* ReflectiveRapidJSON::JsonReflector instead.
|
||||||
|
*/
|
||||||
|
#define REFLECTIVE_RAPIDJSON_MAKE_JSON_SERIALIZABLE(T) \
|
||||||
|
template <> struct ::ReflectiveRapidJSON::AdaptedJsonSerializable<T> : Traits::Bool<true> { \
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \def The REFLECTIVE_RAPIDJSON_PUSH_PRIVATE_MEMBERS macro enables serialization of private members.
|
||||||
|
* \remarks For an example, see README.md.
|
||||||
|
*/
|
||||||
|
#define REFLECTIVE_RAPIDJSON_PUSH_PRIVATE_MEMBERS(T) \
|
||||||
|
friend void ::ReflectiveRapidJSON::JsonReflector::push<T>( \
|
||||||
|
const T &reflectable, ::RAPIDJSON_NAMESPACE::Value::Object &value, ::RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator)
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \def The REFLECTIVE_RAPIDJSON_PULL_PRIVATE_MEMBERS macro enables deserialization of private members.
|
||||||
|
* \remarks For an example, see README.md.
|
||||||
|
*/
|
||||||
|
#define REFLECTIVE_RAPIDJSON_PULL_PRIVATE_MEMBERS(T) \
|
||||||
|
friend void ::ReflectiveRapidJSON::JsonReflector::pull<T>(T & reflectable, \
|
||||||
|
const ::RAPIDJSON_NAMESPACE::GenericValue<::RAPIDJSON_NAMESPACE::UTF8<char>>::ConstObject &value, \
|
||||||
|
::ReflectiveRapidJSON::JsonDeserializationErrors *errors)
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \def The REFLECTIVE_RAPIDJSON_ENABLE_PRIVATE_MEMBERS macro enables serialization and deserialization of private members.
|
||||||
|
* \remarks For an example, see README.md.
|
||||||
|
*/
|
||||||
|
#define REFLECTIVE_RAPIDJSON_ENABLE_PRIVATE_MEMBERS(T) \
|
||||||
|
REFLECTIVE_RAPIDJSON_PUSH_PRIVATE_MEMBERS(T); \
|
||||||
|
REFLECTIVE_RAPIDJSON_PULL_PRIVATE_MEMBERS(T)
|
||||||
|
|
||||||
} // namespace ReflectiveRapidJSON
|
} // namespace ReflectiveRapidJSON
|
||||||
|
|
||||||
#endif // REFLECTIVE_RAPIDJSON_JSON_SERIALIZABLE_H
|
#endif // REFLECTIVE_RAPIDJSON_JSON_SERIALIZABLE_H
|
||||||
|
|
Loading…
Reference in New Issue