C + + realizes the friendly processing of Json data

Posted by ionik on Sat, 12 Feb 2022 03:28:23 +0100

Python wechat ordering applet course video

https://edu.csdn.net/course/detail/36074

Python actual combat quantitative transaction financial management system

https://edu.csdn.net/course/detail/35475

background

C/C + + client needs to receive and send JSON format data to the back end to realize communication and data interaction. There is no ready-made interface for C + + to process JSON format data, and direct reference to third-party libraries can not avoid disassembly and splicing. Considering that this project will have a large amount of JSON data to process, repetitive splitting and splicing cannot be avoided. Therefore, it is intended to encapsulate a set of interfaces for converting C + + structure objects to JSON data and directly loading JSON data into C + + structure objects, which is similar to the common serialization and deserialization in data transmission, so as to facilitate subsequent data processing and improve development efficiency.

Design

Objectives:

  1. The C + + structure object instance can be converted into JSON string data through a simple interface, or a string of JSON string data can be loaded and assigned to a C + + structure object instance. Ideal interface: Json2Object(inJsonString, outStructObject), or object2json (injoctobject, outjsonstring)
  2. It supports the Json conversion of built-in basic types such as bool, int and double, the Json conversion of user-defined structures, the Json conversion of the above types as element arrays, and the Json conversion of nested structures

effect:

Unit test code first

TEST_CASE("Parse structure array to JSON strand", "[json]")
{
    struct DemoChildrenObject
 {
        bool boolValue;
        int intValue;
        std::string strValue;
        /*JSON Mutual conversion member variable declaration (required)*/
        JSONCONVERT2OBJECT_MEMEBER_REGISTER(boolValue, intValue, strValue)
    };

    struct DemoObjct
 {
        bool boolValue;
        int intValue;
        std::string strValue;
        /*Nested structure supporting JSON conversion, member variables, array form*/
        std::vector< DemoChildrenObject> children;
        
        /*JSON Mutual conversion member variable declaration (required)*/
        JSONCONVERT2OBJECT_MEMEBER_REGISTER(boolValue, intValue, strValue, children)
    };

    DemoObjct demoObj;
    /*Start assigning values to the member variables of the demoObj object*/
    demoObj.boolValue = true;
    demoObj.intValue = 321;
    demoObj.strValue = "hello worLd";

    DemoChildrenObject child1;
    child1.boolValue = true;
    child1.intValue = 1000;
    child1.strValue = "hello worLd child1";

    DemoChildrenObject child2;
    child2.boolValue = true;
    child2.intValue = 30005;
    child2.strValue = "hello worLd child2";

    demoObj.children.push_back(child1);
    demoObj.children.push_back(child2);
    /*End the assignment of the member variable of demoObj object*/

    std::string jsonStr;
    /*Key conversion function*/
    REQUIRE(Object2Json(jsonStr, demoObj)); 
    std::cout << "returned json format: " << jsonStr << std::endl;

    /*The printed content is as follows:
 returned json format: {
 "boolValue" : true,
 "children" : [
 {
 "boolValue" : true,
 "intValue" : 1000,
 "strValue" : "hello worLd child1"
 },
 {
 "boolValue" : true,
 "intValue" : 30005,
 "strValue" : "hello worLd child2"
 }
 ],
 "intValue" : 321,
 "strValue" : "hello worLd"
 }
 */
    
    DemoObjct demoObj2;
    /*Key conversion function*/
    REQUIRE(Json2Object(demoObj2, jsonStr));

    /*Verify whether the contents of each member variable in the converted structure variable are as expected*/
    REQUIRE(demoObj2.boolValue == true);
    REQUIRE(demoObj2.intValue == 321);
    REQUIRE(demoObj2.strValue == "hello worLd");

    REQUIRE(demoObj2.children.size() == 2);

    REQUIRE(demoObj.children[0].boolValue == true);
    REQUIRE(demoObj.children[0].intValue == 1000);
    REQUIRE(demoObj.children[0].strValue == "hello worLd child1");

    REQUIRE(demoObj.children[1].boolValue == true);
    REQUIRE(demoObj.children[1].intValue == 30005);
    REQUIRE(demoObj.children[1].strValue == "hello worLd child2");
}

realization

This time, we only focus on how to convert the structure and JSON string in a friendly way, rather than how to convert the JSON string to the basic data type. There are already many third-party libraries to help us solve this problem, such as cJSON,Jsoncpp,rapidjson , you don't have to build wheels again.

This time, we chose JsonCPP as the underlying JSON parsing support. If you want to replace it with other third-party libraries, it is also relatively simple to modify the corresponding embedded content.

Our goal is to achieve two interfaces:

  • Json2Object(inJsonString, outStructObject)
  • Object2Json(inStructObject, outJsonString)

Combined with the types defined by JsonCPP itself, we need to further 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 and string, the implementation is relatively simple:

/*int Type support*/
static bool Json2Object(int& aimObj, const Json::Value& jsonTypeValue)
{
    if (jsonTypeValue.isNull() || !jsonTypeValue.isInt()) {
        return false;
    } else {
        aimObj = jsonTypeValue.asInt();
        return true;
    }
}

static bool Object2Json(Json::Value& jsonTypeValue, const std::string& key, const int& value)
{
    jsonTypeValue[key] = value;
    return true;
}

/*std::string String type support*/
static bool Json2Object(std::string& aimObj, const Json::Value& jsonTypeValue)
{
    if (jsonTypeValue.isNull() || !jsonTypeValue.isString()) {
        return false;
    } else {
        aimObj = jsonTypeValue.asString();
        return true;
    }
}

static bool Object2Json(Json::Value& jsonTypeValue, const std::string& key, const std::string& value)
{
    jsonTypeValue[key] = value;
    return true;
}

Custom data structure type

For a custom structure type, what we need to do is to ensure that its member variables can correspond to JSON nodes one by one and can be matched for data filling.

   /*Json character string:
 {
 "boolValue" : true,
 "intValue" : 1234,
 "strValue" : "demo object!"
 }*/

   struct DemoObjct
 {
        bool boolValue;
        int intValue;
        std::string strValue;
    };

As in the above example, in the process of mutual conversion, "boolValue" can correspond to the member variable named boolValue in the DemoObjct object, and "strValue" can correspond to the member variable named strValue in the DemoObjct object.

Under normal circumstances, for this scenario, we can only implement additional processing functions for DemoObjct structure for data conversion. Because the member variables defined by different structure declarations are different, each structure needs to be implemented separately, which is cumbersome and not universal.

From here, what we need to do is * * "hide" * * the conversion functions implemented for class structures, and make use of the characteristics of the language (function templates, etc.) to let them help us do these things.

  1. Declare the conversion member function. In this member function implementation, each member variable can read or write values from JSON native data.
  2. The purpose of registering member variables is to let the conversion member function know which member variables need to be processed, and each member variable corresponds to which node field in JSON native data, so as to match reading and writing.
  3. When the Json2Object and Object2Json functions are called externally, the conversion member function is triggered to fill in or output the contents of the member variable.

It only takes three steps to put the elephant in the refrigerator. Let's see how to do these three steps.

Member variable processing

  • Considering that the type and number of member variables of each structure are uncontrollable, and each member variable needs to be regarded as an lvalue (when json2oobject), it can not be simply processed by array enumeration. Instead, the variable parameter template, a feature of C++11, can be used to traverse and process each member variable from the inside to the outside
template 
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 
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...);
 }
}

  • There is a corresponding relationship between the member variable and the key field of the JSON native node. For a preliminary consideration, the name of the member variable is first regarded as the key name of the corresponding node in JSON. This can be achieved through the feature defined by the macro. The contents of the declared and registered member variables are split into a key name list as a string.
#define JSONCONVERT2OBJECT\_MEMEBER\_REGISTER(...) \
bool ParseHelpImpl(const Json::Value& jsonTypeValue)
{
    std::vector<std::string> names = Member2KeyParseWithStr(#__VA_ARGS__);
    return JsonParse(names, 0, jsonTypeValue, __VA_ARGS__);
}

Member variable registration

For example, DemoObjct class structure, add JSONCONVERT2OBJECT_MEMEBER_REGISTER with the registration declaration of the member variable:

   struct DemoObjct
 {
        bool boolValue;
        int intValue;
        std::string strValue;
       
       JSONCONVERT2OBJECT_MEMEBER_REGISTER(boolValue, intValue, strValue)
           
    };

Equivalent to:

   struct DemoObjct
 {
        bool boolValue;
        int intValue;
        std::string strValue;
       
       bool ParseHelpImpl(const Json::Value& jsonTypeValue, 
 std::vector<std::string> &names)
        {
            names = Member2KeyParseWithStr("boolValue, intValue, strValue");
            //names gets ["boolValue","intValue", "strValue"]
            //Then take these key s and assign values from Json to member variables one by one
            return JsonParse(names, 0, jsonTypeValue, boolValue, intValue, strValue);
        }      
    };

Template matching prevents compilation errors

So far, the core functions have been realized. If the declaration registration of JSON transformation is not added to the target structure class, the compilation error will be caused when using the Json2Object interface externally, prompting that the declaration definition of ParseHelpImpl member function cannot be found... We can use enable_if to provide default functions for structures that do not declare registered macros.

template ::has, int>::type = 0>
static inline bool Json2Object(TClass& aimObj, const Json::Value& jsonTypeValue)
{
 std::vector<std::string> names = PreGetCustomMemberNameIfExists(aimObj);
 return aimObj.JSONCONVERT2OBJECT\_MEMEBER\_REGISTER\_RESERVERD\_IMPLE(jsonTypeValue, names);
}

template ::has, int>::type = 0>
static inline bool Json2Object(TClass& aimObj, const Json::Value& jsonTypeValue)
{
 return false;
}

Rename member variable matching Key

The current implementation takes the name of the member variable as the key name in the JSON string. For flexible processing, a macro is added to re declare the key corresponding to the JSON string in the structure member variable, for example:

    struct DemoObjct
 {
        bool boolValue;
        int intValue;
        std::string strValue;

        JSONCONVERT2OBJECT_MEMEBER_REGISTER(boolValue, intValue, strValue)
        /*Redeclare the key of the member variable corresponding to the JSON string. Note that the order is consistent*/
        JSONCONVERT2OBJECT_MEMEBER_RENAME_REGISTER("bValue", "iValue", "sValue")
    };

    DemoObjct demoObj;
    /*boolValue <--> bValue; intValue <--> iValue; ...*/
    REQUIRE(Json2Object(demoObj, std::string("{\"bValue\":true, \"iValue\":1234, \"sValue\":\"demo object!\"}")));
    REQUIRE(demoObj.boolValue == true);
    REQUIRE(demoObj.intValue == 1234);
    REQUIRE(demoObj.strValue == "demo object!");

Object2Json implementation

The operations mentioned above are mostly provided to implement the Json2Object interface. The conversion from structure object to Json is also a similar operation. It will not be described here. For details, please refer to the source code.

Highlights

  • Simplify the processing of JSON data by C + +, and pay attention to the operation of splitting and processing JSON data;
  • It provides a simple interface to switch freely from structure to JSON string and from JSON string to structure

Source code


#include "json/json.h"
#include 
#include 
#include 

#define JSONCONVERT2OBJECT\_MEMEBER\_REGISTER(...) \
bool JSONCONVERT2OBJECT\_MEMEBER\_REGISTER\_RESERVERD\_IMPLE(const Json::Value& jsonTypeValue, std::vector &names) \
{ \
 if(names.size() <= 0) { \
 names = Member2KeyParseWithStr(#\_\_VA\_ARGS\_\_); \
 } \
 return JsonParse(names, 0, jsonTypeValue, \_\_VA\_ARGS\_\_); \
} \
bool OBJECTCONVERT2JSON\_MEMEBER\_REGISTER\_RESERVERD\_IMPLE(Json::Value& jsonTypeValue, std::vector &names) const \
{ \
 if(names.size() <= 0) { \
 names = Member2KeyParseWithStr(#\_\_VA\_ARGS\_\_); \
 } \
 return ParseJson(names, 0, jsonTypeValue, \_\_VA\_ARGS\_\_); \
} \


#define JSONCONVERT2OBJECT\_MEMEBER\_RENAME\_REGISTER(...) \
std::vector JSONCONVERT2OBJECT\_MEMEBER\_RENAME\_REGISTER\_RESERVERD\_IMPLE() const \
{ \
 return Member2KeyParseWithMultiParam({ \_\_VA\_ARGS\_\_ }); \
}

namespace JSON
{
template <bool, class TYPE = void>
struct enable\_if
{
};

template <class TYPE>
struct enable\_if<true, TYPE>
{
    typedef TYPE type;
};
} //JSON

template 
struct HasConverFunction
{
 template 
 static char func(decltype(&TT::JSONCONVERT2OBJECT\_MEMEBER\_REGISTER\_RESERVERD\_IMPLE)); //@1
 
 template 
 static int func(...); //@2

 const static bool has = (sizeof(func(NULL)) == sizeof(char));

 template 
 static char func2(decltype(&TT::JSONCONVERT2OBJECT\_MEMEBER\_RENAME\_REGISTER\_RESERVERD\_IMPLE)); //@1
 template 
 static int func2(...); //@2
 const static bool has2 = (sizeof(func2(NULL)) == sizeof(char));
};

static std::vector<std::string> Member2KeyParseWithMultiParam(std::initializer\_list<std::string> il)
{
 std::vector<std::string> result;
 for (auto it = il.begin(); it != il.end(); it++) {
 result.push\_back(*it);
 }
 return result;
}

inline static std::string NormalStringTrim(std::string const& str)
{
 static char const* whitespaceChars = "\n\r\t ";
 std::string::size\_type start = str.find\_first\_not\_of(whitespaceChars);
 std::string::size\_type end = str.find\_last\_not\_of(whitespaceChars);
 return start != std::string::npos ? str.substr(start, 1 + end - start) : std::string();
}

inline static std::vector<std::string> NormalStringSplit(std::string str, char splitElem)
{
 std::vector<std::string> strs;
 std::string::size\_type pos1, pos2;
 pos2 = str.find(splitElem);
 pos1 = 0;
 while (std::string::npos != pos2) {
 strs.push\_back(str.substr(pos1, pos2 - pos1));
 pos1 = pos2 + 1;
 pos2 = str.find(splitElem, pos1);
 }
 strs.push\_back(str.substr(pos1));
 return strs;
}

static std::vector<std::string> Member2KeyParseWithStr(const std::string& values)
{
 std::vector<std::string> result;
 auto enumValues = NormalStringSplit(values, ',');
 result.reserve(enumValues.size());
 for (auto const& enumValue : enumValues) {
 result.push\_back(NormalStringTrim(enumValue));
 }
 return result;
}

//

static bool Json2Object(bool& aimObj, const Json::Value& jsonTypeValue)
{
 if (jsonTypeValue.isNull() || !jsonTypeValue.isBool()) {
 return false;
 } else {
 aimObj = jsonTypeValue.asBool();
 return true;
 }
}
static bool Object2Json(Json::Value& jsonTypeValue, const std::string& key, bool value)
{
 jsonTypeValue[key] = value;
 return true;
}

static bool Json2Object(int& aimObj, const Json::Value& jsonTypeValue)
{
 if (jsonTypeValue.isNull() || !jsonTypeValue.isInt()) {
 return false;
 } else {
 aimObj = jsonTypeValue.asInt();
 return true;
 }
}
static bool Object2Json(Json::Value& jsonTypeValue, const std::string& key, const int& value)
{
 jsonTypeValue[key] = value;
 return true;
}

static bool Json2Object(unsigned int& aimObj, const Json::Value& jsonTypeValue)
{
 if (jsonTypeValue.isNull() || !jsonTypeValue.isUInt()) {
 return false;
 } else {
 aimObj = jsonTypeValue.asUInt();
 return true;
 }
}
static bool Object2Json(Json::Value& jsonTypeValue, const std::string& key, const unsigned int& value)
{
 jsonTypeValue[key] = value;
 return true;
}

static bool Json2Object(double& aimObj, const Json::Value& jsonTypeValue)
{
 if (jsonTypeValue.isNull() || !jsonTypeValue.isDouble()) {
 return false;
 } else {
 aimObj = jsonTypeValue.asDouble();
 return true;
 }
}
static bool Object2Json(Json::Value& jsonTypeValue, const std::string& key, const double& value)
{
 jsonTypeValue[key] = value;
 return true;
}

static bool Json2Object(std::string& aimObj, const Json::Value& jsonTypeValue)
{
 if (jsonTypeValue.isNull() || !jsonTypeValue.isString()) {
 return false;
 } else {
 aimObj = jsonTypeValue.asString();
 return true;
 }
}
static bool Object2Json(Json::Value& jsonTypeValue, const std::string& key, const std::string& value)
{
 jsonTypeValue[key] = value;
 return true;
}

template ::has2, int>::type = 0>
static inline std::vector<std::string> PreGetCustomMemberNameIfExists(const TClass& aimObj)
{
 return aimObj.JSONCONVERT2OBJECT\_MEMEBER\_RENAME\_REGISTER\_RESERVERD\_IMPLE();
}

template ::has2, int>::type = 0>
static inline std::vector<std::string> PreGetCustomMemberNameIfExists(const TClass& aimObj)
{
 return std::vector<std::string>();
}

template ::has, int>::type = 0>
static inline bool Json2Object(TClass& aimObj, const Json::Value& jsonTypeValue)
{
 std::vector<std::string> names = PreGetCustomMemberNameIfExists(aimObj);
 return aimObj.JSONCONVERT2OBJECT\_MEMEBER\_REGISTER\_RESERVERD\_IMPLE(jsonTypeValue, names);
}

template ::has, int>::type = 0>
static inline bool Json2Object(TClass& aimObj, const Json::Value& jsonTypeValue)
{
 return false;
}

template 
static bool Json2Object(std::vector& aimObj, const Json::Value& jsonTypeValue)
{
 if (jsonTypeValue.isNull() || !jsonTypeValue.isArray()) {
 return false;
 } else {
 aimObj.clear();
 bool result(true);
 for (int i = 0; i < jsonTypeValue.size(); ++i) {
 T item;
 if (!Json2Object(item, jsonTypeValue[i])) {
 result = false;
 }
 aimObj.push\_back(item);
 }
 return result;
 }
}

template 
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 
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...);
 }
}

/** Provider interface*/
template
bool Json2Object(TClass& aimObj, const std::string& jsonTypeStr)
{
 Json::Reader reader;
 Json::Value root;

 if (!reader.parse(jsonTypeStr, root) || root.isNull()) {
 return false;
 }
 return Json2Object(aimObj, root);
}

static bool GetJsonRootObject(Json::Value& root, const std::string& jsonTypeStr)
{
 Json::Reader reader;
 if (!reader.parse(jsonTypeStr, root)) {
 return false;
 }
 return true;
}



template ::has, int>::type = 0>
static inline bool Object2Json(Json::Value& jsonTypeOutValue, const std::string& key, const TClass& objValue)
{
 std::vector<std::string> names = PreGetCustomMemberNameIfExists(objValue);
 if (key.empty()) {
 return objValue.OBJECTCONVERT2JSON\_MEMEBER\_REGISTER\_RESERVERD\_IMPLE(jsonTypeOutValue, names);
 } else {
 Json::Value jsonTypeNewValue;
 const bool result = objValue.OBJECTCONVERT2JSON\_MEMEBER\_REGISTER\_RESERVERD\_IMPLE(jsonTypeNewValue, names);
 if (result) {
 jsonTypeOutValue[key] = jsonTypeNewValue;
 }
 return result;
 }
}

template ::has, int>::type = 0>
static inline bool Object2Json(Json::Value& jsonTypeOutValue, const std::string& key, const TClass& objValue)
{
 return false;
}

template 
static bool Object2Json(Json::Value& jsonTypeOutValue, const std::string& key, const std::vector& objValue)
{
 bool result(true);
 for (int i = 0; i < objValue.size(); ++i) {
 Json::Value item;
 if (!Object2Json(item, "", objValue[i])) {
 result = false;
 } else {
 if (key.empty()) jsonTypeOutValue.append(item);
 else jsonTypeOutValue[key].append(item);
 }
 }
 return result;
}

template 
static bool ParseJson(const std::vector<std::string>& names, int index, Json::Value& jsonTypeValue, const T& arg)
{
 if (names.size() > index) {
 const std::string key = names[index];
 return Object2Json(jsonTypeValue, key, arg);
 } else {
 return false;
 }
}

template 
static bool ParseJson(const std::vector<std::string>& names, int index, Json::Value& jsonTypeValue, T& arg, Args&... args)
{
 if (names.size() - (index + 0) != 1 + sizeof...(Args)) {
 return false;
 }
 const std::string key = names[index];
 Object2Json(jsonTypeValue, key, arg);

 return ParseJson(names, index + 1, jsonTypeValue, args...);
}

/** Provider interface*/
template
bool Object2Json(std::string& jsonTypeStr, const T& obj)
{
 //std::functionplacehoder = [&]()->Json::Value { return Json::Value(); };
 //auto func = [&](std::functionf) { return f(); };
 //Json::Value val = func(placehoder);

 Json::StyledWriter writer;
 Json::Value root;
 const bool result = Object2Json(root, "", obj);
 if (result) {
 jsonTypeStr = writer.write(root);
 }
 return result;
}


Reference documents

Topics: C++ Android JSON computer