背景與需求
之前寫C#的時候,決議json字串一般使用的是開源的類別庫Newtonsoft.Json,方法十分簡潔,比如:
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"}");
一行代碼就能將json字串轉為相應的類物件,
最近寫C++需要處理一下json資料,于是上github找了很多很強大的開源庫,像jsoncpp、rapidjson、json,基本上都滿足了開發需求,但想要做成像寫C#那樣子就要做二次開發,于是有了自己寫一個簡單的json轉類 | 結構體的工具的想法,
需求如下:
- 只有頭檔案,方便使用
- 最多三行代碼解決轉換
- 支持類|結構體 與 json的相互轉換
- 支持多種基本資料型別,如int、float、string、bool等
- 支持STL基本型別,如vector、list、map<string,T>等
- 支持嵌套關系
- 支持成員重命名,比方說json中的關鍵字是name,成員命名可寫成Name或其他,
最終使用的樣例代碼
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;
}
實作方法
因為剛好rapidjson只需要頭檔案就可以使用,所以選擇了rapidjson作為基礎庫,進行二次開發,
基礎型別的轉換
作為最底層的介面,只需要進行一個賦值的操作即可,后續如果想要增加一些其他型別支持,添加起來也比較方便,
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;
}
//其他型別... ...
類成員注冊
這里使用宏定義方式 + 可變引數模板的方式來實作,即可依次對注冊的成員進行賦值
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__); \
}
自定義類的轉換
自定義類由于并不清楚外界使用時,是否有按規定添加好成員注冊介面,所以這里采用enable_if的方式來嘗試呼叫,編譯的時候也就不會報錯,
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;
}
外部呼叫介面
/**
* @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);
}
最核心的部分也就上面的幾個模塊,其他的都是一些瑣碎的增加型別支持等操作,
學習與參考
- 騰訊json決議庫:Tencent/rapidjson
- 楊昕: C++ 輕量級物件JSON序列化實作
備注
工程已經放在Github,名字為AIGCJson,有什么建議或者想法的,歡迎提出issue或者pull request
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/152543.html
標籤:其他
下一篇:軟體設計師考試——國慶節篇1
