C++ two lines of code to achieve mutual conversion between json and class objects

Background and needs

When writing C# before, the open source library Newtonsoft.Json was generally used to parse json strings. The method was very concise, such as:

class Project
{
    public string Input { get; set; }
    public string Output { get; set; }
}
JavaScriptSerializer serializer = new JavaScriptSerializer();
Project test = serializer.Deserialize<Project>(@"{"Input":"1","Output":"2"}");

One line of code can convert a json string into a corresponding class object.

Recently, writing C++ needs to process json data, so I found a lot of powerful open source libraries on github, such as jsoncpp , rapidjson , json , which basically meet the development needs, but if you want to do it like writing C#, you have to do it twice. Development. So I had the idea of ​​writing a simple json to class|structure tool by myself.

The requirements are as follows:

  • Only header files, easy to use
  • Up to three lines of code to resolve the conversion
  • Support class|structure and json conversion
  • Support a variety of basic data types, such as int, float, string, bool, etc.
  • Support STL basic types, such as vector, list, map<string, T>, etc.
  • Support nested relationship
  • Support member renaming, for example, the keyword in json is name, and member naming can be written as Name or others.

The final sample code

class Student
{
public:
    string Name;
    int Age;

    AIGC_JSON_HELPER(Name, Age)//成员注册
    AIGC_JSON_HELPER_RENAME("name","age")//成员重命名,不需要可以删除这条
};

int main()
{
    //json转类对象
    Student person;
    JsonHelper::JsonToObject(person, R"({"name":"XiaoMing", "age":15})");
	//类对象转json
    string jsonStr;
    JsonHelper::ObjectToJson(person, jsonStr);
    return 0;
}

Implementation

Because it just happens that rapidjson only needs the header file to be used, so I chose rapidjson as the basic library for secondary development.

Basic type conversion

As the lowest-level interface, you only need to perform an assignment operation. If you want to add some other types of support later, it is more convenient to add it.

static bool JsonToObject(int &obj, rapidjson::Value &jsonValue)
{
    if (jsonValue.IsNull() || !jsonValue.IsInt())
        return false;
    obj = jsonValue.GetInt();
    return true;
}

static bool JsonToObject(unsigned int &obj, rapidjson::Value &jsonValue)
{
    if (jsonValue.IsNull() || !jsonValue.IsUint())
        return false;
    obj = jsonValue.GetUint();
    return true;
}

static bool JsonToObject(int64_t &obj, rapidjson::Value &jsonValue)
{
    if (jsonValue.IsNull() || !jsonValue.IsInt64())
        return false;
    obj = jsonValue.GetInt64();
    return true;
}

//其他类型... ...

Class member registration

Here we use the macro definition method + variable parameter template method to achieve, you can assign values ​​to the registered members in turn

template <typename TYPE, typename... TYPES>
static bool WriteMembers(std::vector<std::string> &names, int index, rapidjson::Value &jsonValue, TYPE &arg, TYPES &... args)
{
    if (!WriteMembers(names, index, jsonValue, arg))
        return false;
    return WriteMembers(names, ++index, jsonValue, args...);
}

template <typename TYPE>
static bool WriteMembers(std::vector<std::string> &names, int index, rapidjson::Value &jsonValue, TYPE &arg)
{
    const char *key = names[index].c_str();
    if (!jsonValue.HasMember(key))
        return true;

    if (!JsonToObject(arg, jsonValue[key]))
        return false;
    return true;
}
#define AIGC_JSON_HELPER(...)  \
bool AIGC_CONVER_JSON_TO_OBJECT(rapidjson::Value &jsonValue, std::vector<std::string> &names) \
{     \
    if (names.size() <= 0)  \
        names = aigc::JsonHelper::GetMembersNames(#__VA_ARGS__); \
    return aigc::JsonHelper::WriteMembers(names, 0, jsonValue, __VA_ARGS__); \
}                                                                                                                                            

Conversion of custom classes

Since the custom class is not clear whether the member registration interface is added as required when used by the outside world, the enable_if method is used here to try to call it, and no error will be reported during compilation.

template <bool, class TYPE = void>
struct enable_if
{
};

template <class TYPE>
struct enable_if<true, TYPE>
{
    typedef TYPE type;
};

template <typename T>
struct HasConverFunction
{
    template <typename TT> static char func(decltype(&TT::AIGC_CONVER_JSON_TO_OBJECT));
    template <typename TT> static int func(...);
    const static bool has = (sizeof(func<T>(NULL)) == sizeof(char));
};

template <typename T, typename enable_if<HasConverFunction<T>::has, int>::type = 0>
static inline bool JsonToObject(T &obj, rapidjson::Value &jsonValue)
{
    std::vector<std::string> names = LoadRenameArray(obj);
    return obj.AIGC_CONVER_JSON_TO_OBJECT(jsonValue, names);
}

template <typename T, typename enable_if<!HasConverFunction<T>::has, int>::type = 0>
static inline bool JsonToObject(T &obj, rapidjson::Value &jsonValue)
{
    return false;
}

External call interface

/**
 * @brief conver json string to class | struct
 * @param obj : class or struct
 * @param jsonStr : json string 
 */
template <typename T>
static inline bool JsonToObject(T &obj, const std::string &jsonStr)
{
    rapidjson::Document root;
    root.Parse(jsonStr.c_str());
    if (root.IsNull())
        return false;

    return JsonToObject(obj, root);
}

The most core part is the above modules, and the others are trivial operations such as adding type support.

Learn and quote

  1. Tencent json parsing library: Tencent / rapidjson
  2. Yang Xin: Implementation of C++ Lightweight Object JSON Serialization

Remarks

The project has been placed on Github with the name AIGCJson . Any suggestions or ideas are welcome to raise an issue or pull request

Guess you like

Origin blog.csdn.net/u014541617/article/details/108903440