2017-10-28 18:24:12 +02:00
|
|
|
# Reflective RapidJSON
|
2017-10-18 23:07:17 +02:00
|
|
|
|
2017-10-24 18:00:51 +02:00
|
|
|
The main goal of this project is to provide a code generator for serializing/deserializing C++ objects to/from JSON
|
|
|
|
using Clang and RapidJSON.
|
|
|
|
|
2017-10-25 19:16:37 +02:00
|
|
|
However, extending the generator to generate code for other applications of reflection or to provide generic
|
2017-11-02 15:15:29 +01:00
|
|
|
reflection would be possible as well.
|
2017-10-25 19:16:37 +02:00
|
|
|
|
|
|
|
This repository also contains a small, additional header to use RapidJSON with Boost.Hana. This allows to serialize
|
|
|
|
or dezerialize simple data structures using the `BOOST_HANA_DEFINE_STRUCT` macro rather than requiring the code
|
|
|
|
generator.
|
2017-10-24 18:00:51 +02:00
|
|
|
|
2017-10-29 23:46:11 +01:00
|
|
|
## Supported datatypes
|
|
|
|
The following table shows the mapping of supported C++ types to supported JSON types:
|
|
|
|
|
|
|
|
| C++ type | JSON type |
|
|
|
|
| ---------------------------------------- |:---------:|
|
|
|
|
| custom structures/classes | object |
|
|
|
|
| bool | true/false|
|
|
|
|
| signed and unsigned integral types | number |
|
|
|
|
| float and double | number |
|
|
|
|
| enum and enum class | number |
|
|
|
|
| std::string | string |
|
|
|
|
| const char * | string |
|
|
|
|
| iteratables (std::vector, std::list, ...)| array |
|
|
|
|
| std::tuple | array |
|
|
|
|
|
|
|
|
### Remarks
|
|
|
|
* `const char *` is only supported for serialization.
|
|
|
|
* Enums are only supported for serialization.
|
|
|
|
* For deserialization, iteratables must provide an `emplace_back` method. So deserialization of eg. `std::forward_list`
|
|
|
|
is currently not supported.
|
2017-11-02 15:15:29 +01:00
|
|
|
* Using smart pointers is not supported yet.
|
2017-10-29 23:46:11 +01:00
|
|
|
* The JSON type `null` is not supported yet.
|
2017-11-02 15:15:29 +01:00
|
|
|
* See also TODOs.md.
|
2017-10-29 23:46:11 +01:00
|
|
|
|
2017-10-24 18:00:51 +02:00
|
|
|
## Usage
|
|
|
|
This example shows how the library can be used to make a `struct` serializable:
|
|
|
|
```
|
2017-10-28 16:23:39 +02:00
|
|
|
#include <reflective-rapidjson/json/serializable.h>
|
2017-10-25 20:03:58 +02:00
|
|
|
|
|
|
|
// define structures, eg.
|
2017-10-28 16:23:39 +02:00
|
|
|
struct TestObject : public JsonSerializable<TestObject> {
|
2017-10-25 20:03:58 +02:00
|
|
|
int number;
|
|
|
|
double number2;
|
|
|
|
vector<int> numbers;
|
|
|
|
string text;
|
|
|
|
bool boolean;
|
|
|
|
};
|
2017-10-28 16:23:39 +02:00
|
|
|
struct NestingObject : public JsonSerializable<NestingObject> {
|
2017-10-25 20:03:58 +02:00
|
|
|
string name;
|
|
|
|
TestObject testObj;
|
|
|
|
};
|
2017-10-28 16:23:39 +02:00
|
|
|
struct NestingArray : public JsonSerializable<NestingArray> {
|
2017-10-25 20:03:58 +02:00
|
|
|
string name;
|
|
|
|
vector<TestObject> testObjects;
|
|
|
|
};
|
|
|
|
|
|
|
|
// serialize to JSON
|
|
|
|
NestingArray obj{ ... };
|
|
|
|
cout << "JSON: " << obj.toJson().GetString();
|
|
|
|
|
|
|
|
// deserialize from JSON
|
|
|
|
const auto obj = NestingArray::fromJson(...);
|
|
|
|
|
|
|
|
// in exactly one of the project's translation units
|
|
|
|
#include "reflection/code-defining-structs.h"
|
2017-10-24 18:00:51 +02:00
|
|
|
```
|
|
|
|
|
2017-10-25 20:03:58 +02:00
|
|
|
Note that the header included at the bottom must be generated by invoking the code generator appropriately, eg.:
|
2017-10-24 18:00:51 +02:00
|
|
|
```
|
2017-10-25 20:03:58 +02:00
|
|
|
reflective_rapidjson_generator -i "$srcdir/code-defining-structs.cpp" -o "$builddir/reflection/code-defining-structs.h"
|
2017-10-24 18:00:51 +02:00
|
|
|
```
|
|
|
|
|
|
|
|
It is possible to use the provided CMake macro to automate this task:
|
|
|
|
```
|
2017-10-25 20:03:58 +02:00
|
|
|
find_package(reflective-rapidjson REQUIRED)
|
|
|
|
list(APPEND CMAKE_MODULE_PATH ${REFLECTIVE_RAPIDJSON_MODULE_DIRS})
|
|
|
|
include(ReflectionGenerator)
|
|
|
|
|
|
|
|
add_reflection_generator_invocation(
|
|
|
|
INPUT_FILES code-defining-structs.cpp
|
|
|
|
GENERATORS json
|
|
|
|
OUTPUT_LISTS LIST_OF_GENERATED_HEADERS
|
|
|
|
)
|
2017-10-24 18:00:51 +02:00
|
|
|
```
|
|
|
|
|
2017-10-25 20:03:58 +02:00
|
|
|
This will produce the file `code-defining-structs.h` in the directory `reflection` in the current build directory. So
|
|
|
|
make sure the current build directory is added to the include directories of your target. The default output directory can
|
|
|
|
also be overridden by passing `OUTPUT_DIRECTORY custom/directory` to the arguments.
|
|
|
|
|
|
|
|
It is possible to specify multiple input files at once. A separate output file is generated for each input. The output files
|
|
|
|
will always have the extension "`.h`", independently of the extension of the input file.
|
|
|
|
|
|
|
|
The full paths of the generated files are also appended to the variable `LIST_OF_GENERATED_HEADERS` which then can be added
|
|
|
|
to the sources of your target. Of course this can be skipped if not required/wanted.
|
|
|
|
|
|
|
|
### Using Boost.Hana instead of the code generator
|
|
|
|
The same example as above. However, this time Boost.Hana is used - so it doesn't require invoking the generator.
|
2017-10-24 18:00:51 +02:00
|
|
|
|
|
|
|
```
|
2017-10-28 16:23:39 +02:00
|
|
|
#include "<reflective-rapidjson/json/serializable-boosthana.h>
|
2017-10-25 20:03:58 +02:00
|
|
|
|
|
|
|
// define structures using BOOST_HANA_DEFINE_STRUCT, eg.
|
2017-10-28 16:23:39 +02:00
|
|
|
struct TestObject : public JsonSerializable<TestObject> {
|
2017-10-25 20:03:58 +02:00
|
|
|
BOOST_HANA_DEFINE_STRUCT(TestObject,
|
|
|
|
(int, number),
|
|
|
|
(double, number2),
|
|
|
|
(vector<int>, numbers),
|
|
|
|
(string, text),
|
|
|
|
(bool, boolean)
|
|
|
|
);
|
|
|
|
};
|
2017-10-28 16:23:39 +02:00
|
|
|
struct NestingObject : public JsonSerializable<NestingObject> {
|
2017-10-25 20:03:58 +02:00
|
|
|
BOOST_HANA_DEFINE_STRUCT(NestingObject,
|
|
|
|
(string, name),
|
|
|
|
(TestObject, testObj)
|
|
|
|
);
|
|
|
|
};
|
2017-10-28 16:23:39 +02:00
|
|
|
struct NestingArray : public JsonSerializable<NestingArray> {
|
2017-10-25 20:03:58 +02:00
|
|
|
BOOST_HANA_DEFINE_STRUCT(NestingArray,
|
|
|
|
(string, name),
|
|
|
|
(vector<TestObject>, testObjects)
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
// serialize to JSON
|
|
|
|
NestingArray obj{ ... };
|
|
|
|
cout << "JSON: " << obj.toJson().GetString();
|
|
|
|
|
|
|
|
// deserialize from JSON
|
|
|
|
const auto obj = NestingArray::fromJson(...);
|
2017-10-24 18:00:51 +02:00
|
|
|
```
|
|
|
|
|
2017-10-28 16:23:39 +02:00
|
|
|
So beside the `BOOST_HANA_DEFINE_STRUCT` macro, the usage remains the same.
|
2017-10-25 20:03:58 +02:00
|
|
|
|
2017-11-02 15:15:29 +01:00
|
|
|
#### Disadvantages
|
|
|
|
* Use of ugly macro required
|
|
|
|
* No context information for errors like type-mismatch available
|
|
|
|
* Inherited members not considered
|
|
|
|
* Support for enums is unlikely
|
|
|
|
|
|
|
|
### Further examples
|
|
|
|
Checkout the test cases for further examples. Relevant files are in
|
|
|
|
the directories `lib/tests` and `generator/tests`.
|
|
|
|
|
2017-11-03 17:45:16 +01:00
|
|
|
### Custom (de)serialization
|
|
|
|
Sometimes it is appropriate to implement custom (de)serialization. For instance, a
|
|
|
|
custom object representing a time value should likey be serialized as a string rather
|
|
|
|
than an object with the internal data members.
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
2017-10-24 18:00:51 +02:00
|
|
|
## Install instructions
|
|
|
|
|
|
|
|
### Dependencies
|
2017-11-02 15:15:29 +01:00
|
|
|
The following dependencies are required at build time. Note that Reflective RapidJSON itself
|
|
|
|
and *none* of these dependencies are required at runtime by an application which makes use of
|
|
|
|
Reflective RapidJSON.
|
|
|
|
|
2017-10-24 18:00:51 +02:00
|
|
|
* C++ compiler and standard library supporting at least C++14
|
2017-11-02 15:15:29 +01:00
|
|
|
* the [CMake](https://cmake.org) build system
|
|
|
|
* LibTooling from [Clang](https://clang.llvm.org) for the code generator
|
|
|
|
* [RapidJSON](https://github.com/Tencent/rapidjson) for JSON (de)serialization
|
|
|
|
* [Boost.Hana](http://www.boost.org/doc/libs/1_65_1/libs/hana/doc/html/index.html) for using
|
|
|
|
`BOOST_HANA_DEFINE_STRUCT` instead of code generator
|
|
|
|
* [C++ utilities](https://github.com/Martchus/cpp-utilities) for various helper functions
|
|
|
|
|
|
|
|
#### Optional
|
|
|
|
* [CppUnit](https://www.freedesktop.org/wiki/Software/cppunit) for building and running the tests
|
|
|
|
* [Doxygen](http://www.doxygen.org) for generating API documentation
|
|
|
|
* [Graphviz](http://www.graphviz.org) for diagrams in the API documentation
|
|
|
|
|
|
|
|
#### Remarks
|
|
|
|
* It is not required to use CMake as build system for your own project. However, when using a
|
|
|
|
different build system, there is no helper for adding the code generator to the build process
|
|
|
|
provided (so far).
|
2017-10-24 18:00:51 +02:00
|
|
|
|
2017-11-02 15:15:29 +01:00
|
|
|
### How to build
|
|
|
|
#### 1. Install dependencies
|
|
|
|
Install all required dependencies. Under a typical GNU/Linux system most of these dependencies
|
|
|
|
can be installed via the package manager. Otherwise follow the links in the "Dependencies" section
|
|
|
|
above.
|
2017-10-24 18:00:51 +02:00
|
|
|
|
2017-11-02 15:15:29 +01:00
|
|
|
C++ utilities is likely not available as package. However, it is possible to build C++ utilities
|
|
|
|
together with `reflective-rapidjson` to simplify the build process. The following build script makes
|
|
|
|
use of this. (To use system C++ utilities, just skip any lines with "`c++utilities`" in the following
|
|
|
|
examples.)
|
2017-10-24 18:00:51 +02:00
|
|
|
|
2017-11-02 15:15:29 +01:00
|
|
|
#### 2. Make dependencies available
|
|
|
|
|
|
|
|
When installing (some) of the dependencies at custom locations, it is likely neccassary to tell
|
|
|
|
CMake where to find them. If you installed everything using packages provided by the system,
|
|
|
|
you can skip this step of course.
|
|
|
|
|
|
|
|
To specify custom locations, just set some environment variables before invoking CMake. This
|
|
|
|
can likely be done in your IDE settings and of course at command line. Here is a Bash example:
|
|
|
|
```
|
|
|
|
export PATH=$CUSTOM_INSTALL_PREFIX/bin:$PATH
|
|
|
|
export CMAKE_PREFIX_PATH=$CUSTOM_INSTALL_PREFIX:$CMAKE_PREFIX_PATH
|
|
|
|
export CMAKE_LIBRARY_PATH=$CUSTOM_INSTALL_PREFIX/lib:$CMAKE_LIBRARY_PATH
|
|
|
|
export CMAKE_INCLUDE_PATH=$CUSTOM_INSTALL_PREFIX/include:$CMAKE_INCLUDE_PATH
|
|
|
|
```
|
|
|
|
|
|
|
|
There are also a lot of [useful variables](https://cmake.org/Wiki/CMake_Useful_Variables)
|
|
|
|
that can be specified as CMake arguments. It is also possible to create a
|
|
|
|
[toolchain file](https://cmake.org/cmake/help/v3.10/manual/cmake-toolchains.7.html).
|
2017-10-24 18:00:51 +02:00
|
|
|
|
|
|
|
|
2017-11-02 15:15:29 +01:00
|
|
|
#### 3. Get sources, eg. using Git:
|
2017-10-24 18:00:51 +02:00
|
|
|
```
|
2017-10-28 18:24:12 +02:00
|
|
|
cd $SOURCES
|
|
|
|
git clone https://github.com/Martchus/cpp-utilities.git c++utilities
|
|
|
|
git clone https://github.com/Martchus/reflective-rapidjson.git
|
2017-10-24 18:00:51 +02:00
|
|
|
```
|
2017-10-28 18:24:12 +02:00
|
|
|
|
|
|
|
If you don't want to build the development version, just checkout the desired version tag.
|
|
|
|
|
2017-11-02 15:15:29 +01:00
|
|
|
#### 4. Run the build script
|
|
|
|
Here is an example for building with GNU Make:
|
2017-10-28 18:24:12 +02:00
|
|
|
```
|
|
|
|
cd $BUILD_DIR
|
|
|
|
# generate Makefile
|
|
|
|
cmake \
|
|
|
|
-DCMAKE_BUILD_TYPE:STRING=Release \
|
|
|
|
-DCMAKE_INSTALL_PREFIX:PATH="/final/install/prefix" \
|
|
|
|
-DBUNDLED_CPP_UTILITIES_PATH:PATH="$SOURCES/c++utilities" \
|
|
|
|
"$SOURCES/reflective-rapidjson"
|
|
|
|
# build library and generators
|
|
|
|
make
|
2017-10-28 22:52:31 +02:00
|
|
|
# build and run tests (optional, requires CppUnit)
|
2017-10-28 18:24:12 +02:00
|
|
|
make check
|
2017-10-28 22:52:31 +02:00
|
|
|
# build tests but do not run them (optional, requires CppUnit)
|
|
|
|
make tests
|
|
|
|
# generate API documentation (optional, reqquires Doxygen)
|
2017-10-28 18:24:12 +02:00
|
|
|
make apidoc
|
|
|
|
# install header files, libraries and generator
|
|
|
|
make install DESTDIR="/temporary/install/location"
|
|
|
|
```
|
|
|
|
Add eg. `-j$(nproc)` to `make` arguments for using all cores.
|