Background
C/C++ client needs to receive and send JSON format data to the backend to achieve communication and data interaction, but there is no ready-made interface for handling JSON format data in C++, so we can’t avoid splitting and splicing by directly referring to third-party libraries. Considering that there will be a lot of JSON data to be processed in this project, we can’t avoid the repetitive splitting and splicing. So I intend to encapsulate a set of C++ structure object to JSON data, JSON data directly mounted on the interface of C++ structure object, similar to the common serialization and deserialization in data transfer, in order to facilitate the subsequent processing of data and improve development efficiency.
Design
Objectives
- To be able to convert a C++ structure object instance to JSON string data, or to load and assign a string of JSON string data to a C++ structure object instance through a simple interface. Ideal interface:
Json2Object(inJsonString, outStructObject)
, orObject2Json(inStructObject, outJsonString)
. 2. - Support Json conversion of built-in basic types such as bool, int, double, Json conversion of custom structures, Json conversion of the above types as arrays of elements, and Json conversion of nested structures.
Effect
first post the unit test code:
|
|
Implementation
This time we will only focus on how to convert between structures and Json strings in a friendly way, without focusing in depth on how exactly JSon strings are converted with basic data types. There are already quite a few third-party libraries to help us with this, such as cJSON, Jsoncpp, rapidjson, without having to create tools repeatedly.
This time we chose JsonCPP as the underlying JSON parsing support, if you want to replace it with other three-party libraries is also relatively simple, modify the corresponding embedded content can be.
Our goal is to implement two interfaces.
Json2Object(inJsonString, outStructObject)
Object2Json(inStructObject, outJsonString)
Combined with the types defined by JsonCPP itself, we further need to implement
Json2Object(const Json::Value& jsonTypeValue, outStructObject)
Object2Json(inStructObject, const std::string& key, Json::Value& jsonTypeValue)
Basic data type conversion
For basic data types such as bool, int, double, string, etc., the implementation is relatively simple.
|
|
Custom data structure type
For the custom structure type, all we have to do is to make sure that its member variables can correspond to the JSON nodes one by one and can match for data filling.
As the above example, in the process of mutual conversion, “boolValue” can correspond to the member variable named boolValue in the DemoObjct object, and “strValue” corresponds to the member variable named strValue in the DemoObjct object.
Under normal circumstances, for this scenario, we can only implement the processing function for data conversion for DemoObjct structure extra, because the member variables defined by different structure declarations are different, so for each structure need to be implemented separately, the work is tedious and not universal.
From here, what we need to do is “hide “ the conversion functions implemented for class structures and use the language’s own features (function templates, etc.) to let them do this for us.
- declare the conversion member functions, and in this member function implementation, enable each member variable to read or write values from the JSON native data.
- register member variables so that the conversion member function knows which member variables it needs to handle and which node field in the JSON native data each member variable corresponds to in order to match reads and writes.
- trigger calls to this conversion member function when the
Json2Object
andObject2Json
functions are called externally in order to populate or output the contents of the member variables.
Fitting the elephant into the fridge takes only three steps, let’s see how these three steps work.
Member Variable Handling
-
Considering the uncontrollable type and number of member variables per structure and the need to treat each member variable as a left value (when
Json2Object
), you can’t simply use array enumeration to handle them, you can use C++11’s feature, variable parameter template, to iterate through each member variable from inside to outside1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
template <typename T> static bool JsonParse(const std::vector<std::string>& names, int index, const Json::Value& jsonTypeValue, T& arg) { const auto key = names[index]; if (!jsonTypeValue.isMember(key) || Json2Object(arg, jsonTypeValue[key])) { return true; } else { return false; } } template <typename T, typename... Args> static bool JsonParse(const std::vector<std::string>& names, int index, const Json::Value& jsonTypeValue, T& arg, Args&... args) { if (!JsonParse(names, index, jsonTypeValue, arg)) { return false; } else { return JsonParse(names, index + 1, jsonTypeValue, args...); } }
-
Member variables have correspondence with the JSON native node key field. Initially, we will first simply consider the member variable name as the key name of the corresponding node in JSON first. This can be achieved by the macro definition feature, which splits the list of key names by declaring the content of the registered member variable as a string.
Member Variable Registration
For example, for the class structure DemoObjct
, add JSONCONVERT2OBJECT_MEMEBER_REGISTER
with a registration statement for the member variable.
Equivalent to.
|
|
Template matching to prevent compilation errors
So far, the core functionality has been implemented. If the target struct class does not add the JSON conversion declaration registration, external use of the Json2Object
interface will result in a compilation error indicating that the declaration definition of the member function ParseHelpImpl
cannot be found. …… We can use enable_if
to provide a default function for to provide default functions for structs that do not declare registered macros.
|
|
Member variable matching Key renaming
The current implementations all use the name of the member variable as the name of the Key in the JSON string. For flexible handling, another macro is added for redeclaring the key corresponding to the JSON string in the member variable of the structure, for example.
|
|
Object2Json implementation
The above mentioned is mostly for the implementation of the Json2Object
interface provided by the operation, from the structure object to Json is also a similar operation, here will not elaborate, detailed can refer to the source code.
Highlights
- Simplify the processing of JSON data in C++, shield attention to the operation of splitting and processing JSON data.
- Provide easy interface to switch from structure to JSON string and JSON string to structure freely.
Source code
|
|