C++對象的JSON序列化與反序列化探索

一:背景

作爲一名C++開發人員,我一直很期待能夠像C#JAVA那樣,可以輕鬆的進行對象的序列化與反序列化,但到目前爲止,尚未找到相對完美的解決方案。

本文旨在拋磚引玉,期待有更好的解決方案;同時向大家尋求幫助,解決本文中未解決的問題。 

二:相關技術介紹

本方案採用JsonCpp來做具體的JSON的讀入與輸出,再結合類成員變量的映射,最終實現對象的JSON序列化與反序列化。

本文不再討論如何使用JsonCpp,此處將作者在應用時發現的兩處問題進行說明:

1.       下載Jsoncpp,編譯其lib,並且引用到項目中,發現有如下錯誤:

錯誤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

錯誤2       fatal error LNK1257: 代碼生成失敗     JasonSerialize 

 

 可以通過在修改LIB庫項目的屬性解決,如下圖[關閉彙編輸出]

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.       目前只支持單個簡單對象[其屬性均爲簡單類型]的序列化與反序列化,暫時未考慮如何支持複雜的,如內部包含其他的複雜對象、包含數組等情況。 

完整代碼請於如下鏈接下載:

 http://download.csdn.net/detail/tragicguy/5630473

發佈了36 篇原創文章 · 獲贊 24 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章