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
|
||||
* Inherited members not considered
|
||||
* Support for enums is unlikely
|
||||
* Attempt to access private members can not be prevented
|
||||
|
||||
### Enable reflection for 3rd party classes/structs
|
||||
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>("...");
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
### Further examples
|
||||
Checkout the test cases for further examples. Relevant files are in
|
||||
the directories `lib/tests` and `generator/tests`.
|
||||
By the way, the functions in the `ReflectiveRapidJSON::JsonReflector` namespace can also
|
||||
be used when inheriting from `JsonSerializable` (instead of the member functions).
|
||||
|
||||
### (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
|
||||
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
|
||||
`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
|
||||
|
||||
### Dependencies
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "../lib/json/serializable.h"
|
||||
|
||||
#include <clang/AST/DeclCXX.h>
|
||||
#include <clang/AST/DeclFriend.h>
|
||||
#include <clang/AST/DeclTemplate.h>
|
||||
|
||||
#include <iostream>
|
||||
|
@ -43,7 +44,7 @@ void JsonSerializationCodeGenerator::addDeclaration(clang::Decl *decl)
|
|||
// check for template specializations to adapt a 3rd party class/struct
|
||||
if (decl->getKind() == clang::Decl::Kind::ClassTemplateSpecialization) {
|
||||
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();
|
||||
if (templateArgs.size() != 1 || templateArgs.get(0).getKind() != clang::TemplateArgument::Type) {
|
||||
return; // FIXME: use Clang diagnostics to print warning
|
||||
|
@ -59,7 +60,7 @@ void JsonSerializationCodeGenerator::addDeclaration(clang::Decl *decl)
|
|||
|
||||
// add any other records
|
||||
m_records.emplace_back(record);
|
||||
}
|
||||
} break;
|
||||
case clang::Decl::Kind::Enum:
|
||||
// TODO: add enums
|
||||
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
|
||||
// output, see ../lib/tests/jsonserializable.cpp (code under comment "pretend serialization code...")
|
||||
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
|
||||
os << "// define code for (de)serializing " << relevantClass.qualifiedName << " objects\n";
|
||||
|
||||
|
@ -103,7 +125,7 @@ void JsonSerializationCodeGenerator::generate(ostream &os) const
|
|||
}
|
||||
os << " // push members\n";
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
@ -128,7 +150,7 @@ void JsonSerializationCodeGenerator::generate(ostream &os) const
|
|||
" }\n"
|
||||
" // pull members\n";
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -220,12 +220,11 @@ void JsonGeneratorTests::testCustomSerialization()
|
|||
*/
|
||||
void JsonGeneratorTests::test3rdPartyAdaption()
|
||||
{
|
||||
static_assert(ReflectiveRapidJSON::JsonReflector::AdaptedJsonSerializable<NotJsonSerializable>::value,
|
||||
"can serialize NotJsonSerializable because of adaption macro");
|
||||
static_assert(!ReflectiveRapidJSON::JsonReflector::AdaptedJsonSerializable<OtherNotJsonSerializable>::value,
|
||||
static_assert(
|
||||
ReflectiveRapidJSON::AdaptedJsonSerializable<NotJsonSerializable>::value, "can serialize NotJsonSerializable because of adaption macro");
|
||||
static_assert(!ReflectiveRapidJSON::AdaptedJsonSerializable<OtherNotJsonSerializable>::value,
|
||||
"can not serialize OtherNotJsonSerializable because adaption macro missing");
|
||||
static_assert(!ReflectiveRapidJSON::JsonReflector::AdaptedJsonSerializable<ReallyNotJsonSerializable>::value,
|
||||
"can not serialize ReallyNotJsonSerializable");
|
||||
static_assert(!ReflectiveRapidJSON::AdaptedJsonSerializable<ReallyNotJsonSerializable>::value, "can not serialize ReallyNotJsonSerializable");
|
||||
|
||||
const NotJsonSerializable test;
|
||||
const string str("{\"butSerializableAnyways\":\"useful to adapt 3rd party structs\"}");
|
||||
|
|
|
@ -28,12 +28,19 @@ private:
|
|||
string privateString = "not going to be serialized";
|
||||
};
|
||||
|
||||
class JsonGeneratorTests;
|
||||
|
||||
/*!
|
||||
* \brief The NestedTestStruct struct inherits from JsonSerializable and should hence have functional fromJson()
|
||||
* and toJson() methods. This is asserted in JsonGeneratorTests::testNesting();
|
||||
*/
|
||||
struct NestedTestStruct : public JsonSerializable<NestedTestStruct> {
|
||||
REFLECTIVE_RAPIDJSON_ENABLE_PRIVATE_MEMBERS(NestedTestStruct);
|
||||
friend class JsonGeneratorTests;
|
||||
|
||||
list<vector<TestStruct>> nested;
|
||||
|
||||
private:
|
||||
deque<double> deq;
|
||||
};
|
||||
|
||||
|
|
|
@ -24,26 +24,13 @@
|
|||
namespace ReflectiveRapidJSON {
|
||||
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
|
|
|
@ -98,6 +98,48 @@ const JsonSerializable<Type> &as(const 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
|
||||
|
||||
#endif // REFLECTIVE_RAPIDJSON_JSON_SERIALIZABLE_H
|
||||
|
|
Loading…
Reference in New Issue