一:背景json
做爲一名 C++ 開發人員,我一直很期待可以像 C# 與 JAVA 那樣,能夠輕鬆的進行對象的序列化與反序列化,但到目前爲止,還沒有找到相對完美的解決方案。數組
本文旨在拋磚引玉,期待有更好的解決方案;同時向你們尋求幫助,解決本文中未解決的問題。 函數
二:相關技術介紹測試
本方案採用 JsonCpp 來作具體的 JSON 的讀入與輸出,再結合類成員變量的映射,最終實現對象的 JSON 序列化與反序列化。ui
本文再也不討論如何使用 JsonCpp ,此處將做者在應用時發現的兩處問題進行說明:spa
1. 下載 Jsoncpp ,編譯其 lib ,而且引用到項目中,發現有以下錯誤:.net
錯誤 1 fatal error C1083: Cannot open compiler generated file: '../../build/vs71/release/lib_json\json_writer.asm': No such file or directory c:\Documents and Settings\Administrator\jsoncpp-src-0.6.0-rc2\jsoncpp-src-0.6.0-rc2\src\lib_json\json_writer.cpp orm
錯誤 2 fatal error LNK1257: 代碼生成失敗 JasonSerialize 對象
能夠經過在修改 LIB 庫項目的屬性解決,以下圖 [ 關閉彙編輸出 ] :blog
2. JSONCPP 官網首頁的下載版本是 0.5.0 ,此版本不支持 Int64 等類型,下載版本 jsoncpp-src-0.6.0-rc2 後便可支持 .
三:一個基於 JsonCpp 的序列化與反序列化基類
先看代碼:
#pragma once #include <string> #include <vector> #include "json/json.h" using std::string; using std::vector; struct CJsonObejectBase { protected: enum CEnumJsonTypeMap { asInt = 1, asUInt, asString, asInt64, asUInt64, }; public: CJsonObejectBase(void){} public: virtual ~CJsonObejectBase(void){} string Serialize() { Json::Value new_item; int nSize = m_listName.size(); for (int i=0; i < nSize; ++i ) { void* pAddr = m_listPropertyAddr[i]; switch(m_listType[i]) { case asInt: new_item[m_listName[i]] = (*(INT*)pAddr); break; case asUInt: new_item[m_listName[i]] = (*(UINT*)pAddr); break; case asInt64: new_item[m_listName[i]] = (*(LONGLONG*)pAddr); break; case asUInt64: new_item[m_listName[i]] = (*(ULONGLONG*)pAddr); break; case asString: new_item[m_listName[i]] = (*(string*)pAddr); default: //我暫時只支持這幾種類型,須要的能夠自行添加 break; } } Json::FastWriter writer; std::string out2 = writer.write(new_item); return out2; } bool DeSerialize(const char* str) { Json::Reader reader; Json::Value root; if (reader.parse(str, root)) { int nSize = m_listName.size(); for (int i=0; i < nSize; ++i ) { void* pAddr = m_listPropertyAddr[i]; switch(m_listType[i]) { case asInt: (*(INT*)pAddr) = root.get(m_listName[i], 0).asInt(); break; case asUInt: (*(UINT*)pAddr) = root.get(m_listName[i], 0).asUInt(); break; case asInt64: (*(LONGLONG*)pAddr) = root.get(m_listName[i], 0).asInt64(); break; case asUInt64: (*(ULONGLONG*)pAddr) = root.get(m_listName[i], 0).asUInt64(); break; case asString: (*(string*)pAddr) = root.get(m_listName[i], "").asString(); default: //我暫時只支持這幾種類型,須要的能夠自行添加 break; } } return true; } return false; } protected: void SetProperty(string name, CEnumJsonTypeMap type, void* addr) { m_listName.push_back(name); m_listPropertyAddr.push_back(addr); m_listType.push_back(type); } virtual void SetPropertys() = 0; vector<string> m_listName; vector<void*> m_listPropertyAddr; vector<CEnumJsonTypeMap> m_listType; };
此類主要有三個函數:Serialize、DeSerialize及 SetPropertys、SetProperty,其中前兩個函數主要是用來實現對象的序列化與反序列化;SetPropertys是一個純虛函數,如 果一個類須要具有序列化功能,只須要今後類繼承,同時調用SetProperty函數,將各個字段的屬性進行設置便可。
四:使用對象的序列化及反序列化功能
要使對象具體相應功能,須要繼承上述的基類,以下:
struct CTestStruct : public CJsonObejectBase { CTestStruct() { SetPropertys(); } ULONGLONG MsgID; string MsgTitle; string MsgContent; protected: //子類須要實現此函數,而且將相應的映射關係進行設置 virtual void SetPropertys() { SetProperty("MsgID", asUInt64, &MsgID); SetProperty("MsgTitle", asString, &MsgTitle); SetProperty("MsgContent", asString, &MsgContent); } };
繼承後,咱們可使用以下代碼來進行測試
序列化:
void CJasonSerializeDlg::OnBnClickedOk() { CTestStruct stru; stru.MsgID = 11223344; stru.MsgTitle = "黑黑"; stru.MsgContent = "哈哈"; CString strTest = stru.Serialize().c_str(); AfxMessageBox(strTest); }
結果:
反序列化:
void CJasonSerializeDlg::OnBnClickedOk2() { const char* pstr = "{\"MsgContent\":\"哈哈22\",\"MsgID\":11111111111111111,\"MsgTitle\":\"黑黑22\"}"; CTestStruct stru; stru.DeSerialize(pstr); CString strShow = ""; strShow.Format("MsgID:%I64u\r\nMsgTile:%s\r\nMsgContent:%s", stru.MsgID, stru.MsgTitle.c_str(), stru.MsgContent.c_str()); AfxMessageBox(strShow); }
結果:
五:未解決的問題
1. 目前我對屬性的映射採用的是 vector 順序映射的方式,這樣必需在子類中對每個屬性進行設置,是否有宏的策略可使這部分工做更加輕鬆?
2. 目前只支持整型、 64 位整型及字符串類型,須要支持其餘類型,能夠在基類中添加映射便可。
3. 目前只支持單個簡單對象 [ 其屬性均爲簡單類型 ] 的序列化與反序列化,暫時未考慮如何支持複雜的,如內部包含其餘的複雜對象、包含數組等狀況。
完整代碼請於以下連接下載: