使用JsonCpp實現C++數據結構與Json格式的相互轉換

 

在一個項目中,由於客戶端與服務端程序各自採用編程平臺有差別,

在兩者進行Socket網絡通信的時候,就面臨數據包格式的一致性問題。

對於這種不同平臺程序間的通信問題,

當然也可以使用protobuf,thrift等成熟的工具對數據進行序列化和反序列化處理。

但是由於客戶端的框架代碼基於原來Json格式,服務器則是C++數據結構格式,

兩者都已經存在現成的框架代碼,爲了不做大的變動,就在服務器端對通信收發的數據進行格式轉換,

也就是需要將C++數據結構與Json格式的數據互相轉換。

本文是基於JsonCpp 0.6.0-rc2 版本對Json數據進行解析的。(附JsonCpp最新版本下載鏈接)

 

本文相關的完整代碼已經打包上傳CSDN,如需下載請點擊下方鏈接 “JsonCpp的簡易封裝”

JsonCpp的簡易封裝(完整代碼)

下載上述代碼之後進行解壓,在VC++項目中包含解壓的代碼,

然後在需要的地方包含zdJsonWrap.h即可使用,例子如下:

#include "..\..\include\zdJson\zdJsonWrap.h"

ps:這裏沒有將JsonCpp編譯成一個lib庫,而是包含所有源代碼,

如果需要用lib庫的形式,請自行封裝。

 

一)C++數據結構轉換到Json

下面給出C++數據結構轉換到Json格式的主要代碼

int Struct2Json(const char *pStructIn, const Json::Value &stJsonDescIn, std::string &strJsonOut, Json::Value *pJsonResult)
{

	Json::Value jvResult;
	
	if(stJsonDescIn.empty()) return -1;

	//根據json描述,獲取數據結構成員
	if(stJsonDescIn.isArray())
	{
		int nSize = stJsonDescIn.size();

		for (int index = 0;index < nSize; index++)
		{
			const Json::Value &childValue = stJsonDescIn[index];	
			zdConstructValue(pStructIn, childValue, jvResult);
		}
	}
	else if(stJsonDescIn.type()==Json::objectValue)
	{
		Json::Value::Members membs = stJsonDescIn.getMemberNames();
		Json::Value::Members::iterator itr =  membs.begin();
		for(; itr != membs.end(); itr++)
		{
			std::string strName = *itr;
			const Json::Value &jValue = stJsonDescIn[strName];
			zdConstructValue(pStructIn, jValue, jvResult);
		}//end of for
	}
	else
	{
		return -2;
	}

	//數據結構轉換爲Json描述輸出
	JsonRewriteValueTree(jvResult, strJsonOut); 
	if(pJsonResult) *pJsonResult = jvResult;

	//JsonRewriteValueTree(jvResult, strJsonOut, "d:\\test_struct2json.json");
	return 0;
}

以下函數,將對應C++數據類型轉換到Json格式

//根據數據結構的json描述,構造json::value對象
int zdConstructValue(const char *pStructIn, const Json::Value &jValue, Json::Value &jvResult)
{

	if(jValue.isMember(ZD_KEY) && jValue.isMember(ZD_TYPE) &&  jValue.isMember(ZD_OFFSET))
	{
		std::string strKeyName = jValue[ZD_KEY].asString();
		std::string strType = jValue[ZD_TYPE].asString();
		UINT uTypeLen = jValue[ZD_LEN].asUInt();
		size_t nOffset = jValue[ZD_OFFSET].asUInt();
		const char * pBeginPos = pStructIn + nOffset;

		//zdtestlog
		TCHAR tcsValLog[1024] = {0};//for test log only

		if( stricmp(strType.c_str(), "INT") == 0 || stricmp(strType.c_str(), "LONG") == 0)
		{
			union unInt
			{
				int nInt;
				char pInt[4];
			}unVal;
			memcpy(unVal.pInt, pBeginPos, sizeof(unVal.pInt));	
	
			jvResult[strKeyName] = unVal.nInt;

			//zd test log
			wsprintf(tcsValLog, TEXT("Val=%d"), unVal.nInt);
		}
		else if(stricmp(strType.c_str(), "DWORD") == 0 || stricmp(strType.c_str(), "UINT") == 0)
		{
			union unDWord
			{
				unsigned int  uInt32;
				char pInt32[4];
			}unVal;

			memcpy(unVal.pInt32, pBeginPos, sizeof(unVal.pInt32));
			
			jvResult[strKeyName] = unVal.uInt32;

			//zd test log
			wsprintf(tcsValLog, TEXT("Val=%u"), unVal.uInt32);
			
		}
		else if(stricmp(strType.c_str(), "INT64") == 0 || stricmp(strType.c_str(), "LONGLONG") == 0)
		{
			union unInt64
			{
				long long nInt64;
				char pInt64[8];
			}unVal;

			memcpy(unVal.pInt64, pBeginPos, sizeof(unVal.pInt64));

			jvResult[strKeyName] = unVal.nInt64;

			//zd test log
			wsprintf(tcsValLog, TEXT("Val=%I64d"), unVal.nInt64);

		}
		else if(stricmp(strType.c_str(), "WORD") == 0 || stricmp(strType.c_str(), "UINT16") == 0)
		{
			union unWord
			{
				unsigned short uInt16;
				char pInt16[2];
			}unVal;

			memcpy(unVal.pInt16, pBeginPos, sizeof(unVal.pInt16));

			jvResult[strKeyName] = unVal.uInt16;

			//zd test log
			wsprintf(tcsValLog, TEXT("Val=%u"), unVal.uInt16);
		}
		else if(stricmp(strType.c_str(), "SHORT") == 0 || stricmp(strType.c_str(), "INT16") == 0)
		{
			union unWord
			{
				short nInt16;
				char pInt16[2];
			}unVal;

			memcpy(unVal.pInt16, pBeginPos, sizeof(unVal.pInt16));

			jvResult[strKeyName] = unVal.nInt16;
			
			//zd test log
			wsprintf(tcsValLog, TEXT("Val=%d"), unVal.nInt16);
		}
		else if(stricmp(strType.c_str(), "FLOAT") == 0)
		{
			union unFloat
			{
				float  fFloat;
				char pFloat[sizeof(float)];
			}unVal;

			memcpy(unVal.pFloat, pBeginPos, sizeof(unVal.pFloat));

			jvResult[strKeyName] = unVal.fFloat;

			//zd test log
			wsprintf(tcsValLog, TEXT("Val=%f"), unVal.fFloat);
		}
		else if(stricmp(strType.c_str(), "DOUBLE") == 0)
		{
			union unDouble
			{
				double  fDouble;
				char pFloat64[sizeof(double)];
			}unVal;

			memcpy(unVal.pFloat64, pBeginPos, sizeof(unVal.pFloat64));
			//sprintf(szBuf,"%f",unVal.fDouble);

			jvResult[strKeyName] = unVal.fDouble;
			//zd test log
			wsprintf(tcsValLog, TEXT("Val=%f"), unVal.fDouble);
		}
		else if(stricmp(strType.c_str(), "CHAR") == 0)
		{
			union unCharStr
			{	
				char *szBuf;
				//char pstr[1];
			}unVal;

			unVal.szBuf = new char[uTypeLen +1];
			memset(unVal.szBuf, 0, uTypeLen +1);
			
			memcpy(unVal.szBuf, pBeginPos, uTypeLen);			

			jvResult[strKeyName] = unVal.szBuf;

			//zd test log
			CA2CT ctVal(unVal.szBuf);
			wsprintf(tcsValLog, TEXT("Val=%s"), ctVal.m_psz);

			delete []unVal.szBuf;
			unVal.szBuf = NULL;
		}
		else if(stricmp(strType.c_str(), "TCHAR") == 0 || stricmp(strType.c_str(), "WCHAR") == 0)
		{
			union unWCharStr
			{
				wchar_t *tcbuf;
				//wchar_t pstr[1];
			}unVal;

			unVal.tcbuf = new wchar_t[uTypeLen + 1];
			memset(unVal.tcbuf, 0, sizeof(wchar_t) *(uTypeLen + 1));

			_tcsncpy(unVal.tcbuf, (wchar_t*)(pBeginPos), uTypeLen - 1);
			CT2CA csTmp(unVal.tcbuf); //unicode轉換爲ansi

			////////////////////////////////////////////////////////////////
			////轉換爲UTF-8編碼
			//std::string strAnsi =  csTmp.m_psz;
			//std::wstring wPoststr = ANSIToUnicode(strAnsi);
			//const wchar_t* pszUtf16 = wPoststr.c_str();
			//uint32_t nSizeUnicode16 = wPoststr.length();
			//char *pszUtf8=new char[2048];
			//uint32_t nSizeUtf8 = 2048;
			//uint32_t uLen = Unicode16ToUTF8((const uint16_t*)pszUtf16, nSizeUnicode16, pszUtf8, nSizeUtf8); 
			//
			//jvResult[strKeyName] = pszUtf8;
			//delete [] pszUtf8;

			////////////////////////////////////////////////////////////////
			jvResult[strKeyName] = csTmp.m_psz;

			//zd test log
			_tcsncpy(tcsValLog, unVal.tcbuf, sizeof(tcsValLog)/sizeof(wchar_t) - 1);

			delete [] unVal.tcbuf;
			unVal.tcbuf = NULL;

		}
		else if(stricmp(strType.c_str(), "BYTE") == 0)
		{
			union unByteStr
			{	
				BYTE *szBuf;
				//char pstr[1];
			}unVal;
	
			unVal.szBuf = new BYTE[uTypeLen +1];
			memset(unVal.szBuf, 0, uTypeLen +1);

			memcpy(unVal.szBuf, pStructIn + nOffset, uTypeLen);
			if(uTypeLen == 1)
			{
				jvResult[strKeyName] = (WORD)(unVal.szBuf[0]);
			}
			else
			{
				jvResult[strKeyName] = (BYTE *)unVal.szBuf;
			}
			
			//zd test log
			wsprintf(tcsValLog, TEXT("Val=%d"), (BYTE)(unVal.szBuf[0]));

			delete [] unVal.szBuf;
			unVal.szBuf = NULL;
		}
		else if(stricmp(strType.c_str(), "OBJ_ARRAY") == 0)//目前只支持一維簡單數組的解析,暫不支持嵌套數組
		{

			///數組解析//////////////////////////
			Json::Value jValueAry;
			jValueAry = jValue["SUB_ITEM_DESC"];
			DWORD dwItemCount = jValue["SUB_ITEM_COUNT"].asUInt();
			WORD wItemSize = jValue["SUB_ITEM_SIZE"].asUInt();
			
			std::string strAryDesc="";
			Json::Value jvAryResult;
			const char *pOffset = pBeginPos;
			if(jValueAry.isArray())
			{	
				for(int i =0; i < dwItemCount; i++)
				{
					std::string strTmpOut;
					Json::Value tmpJResult;
					Struct2Json(pOffset, jValueAry, strTmpOut, &tmpJResult);
					jvAryResult.append(tmpJResult);
					pOffset += wItemSize;
				}		

				jvResult[strKeyName] = jvAryResult;
			}
			else
			{
				jvResult[strKeyName] = "";
			}

		}
		else //unknown datatype
		{
			//union unCharStr
			//{	
			//	char szBuf[1280];
			//	char pstr[1];
			//}unVal;
			//unVal.szBuf[0] = 0;
			//if(nTypeLen < sizeof(unVal.szBuf))
			//{
			//	memcpy(unVal.szBuf, pStructIn + nOffset, nTypeLen);
			//}
			//else //buf size is not enough
			//{				
			//	memcpy(unVal.szBuf, pStructIn + nOffset, sizeof(szBuf)-1);
			//}

			jvResult[strKeyName] = "";
			
			//zd test log
			wsprintf(tcsValLog, TEXT("Val=%s"), TEXT("Unknown DATA_TYPE"));
		}

		
			

		return 0;
	}
	
	return -1;
}

 

以下代碼給出C++數據結構轉換到Json格式的例子,其中GET_OFFSET宏定義如下:

//獲取數據結構成員的偏移位置
#define GET_OFFSET(stt,memb)  (size_t) &(((stt*)0)->memb)

//此函數爲測試用例
int zdTestStruct2Json()
{
	//測試用的數據結構
	struct STTest
	{
		WORD	nID;
		TCHAR	szName[33];
		float	fHeight;
		double	fWeigth;
		WORD	wAge;
		DWORD	dwProcess;
		BYTE	cbAgent;
	};
	STTest st1;

	//賦值
	st1.nID = 3;
	_tcscpy(st1.szName, TEXT("TEST NAME"));
	st1.fHeight = 1.82;
	st1.fWeigth = 70.5;
	st1.wAge = 26;
	st1.dwProcess = 223123;
	st1.cbAgent =2;

	ZD_DECLARE_JSON_DESC;

	//構造數據結構的json描述
	ZD_SET_JSON_DESC("nID", "WORD", sizeof(st1.nID), GET_OFFSET(STTest,nID));
	ZD_SET_JSON_DESC("szName", "TCHAR", sizeof(st1.szName), GET_OFFSET(STTest,szName));
	ZD_SET_JSON_DESC("fHeight", "FLOAT", sizeof(st1.fHeight), GET_OFFSET(STTest,fHeight));
	ZD_SET_JSON_DESC("fWeigth", "DOUBLE", sizeof(st1.fWeigth), GET_OFFSET(STTest,fWeigth));
	ZD_SET_JSON_DESC("wAge", "WORD", sizeof(st1.wAge), GET_OFFSET(STTest,wAge));
	ZD_SET_JSON_DESC("dwProcess", "DWORD", sizeof(st1.dwProcess), GET_OFFSET(STTest,dwProcess));
	ZD_SET_JSON_DESC("cbAgent", "BYTE", sizeof(st1.cbAgent), GET_OFFSET(STTest,cbAgent));

	//數據結構STTest轉換爲Json格式
	std::string strJsonOut;
	Struct2Json((char *)(&st1), stDesc, strJsonOut);

	//輸出到磁盤文件
	Json::StyledWriter swriter;
	std::ofstream ofs1;
	std::ofstream ofs2;

	std::string strDesc = swriter.write(stDesc);

	ofs1.open("d:\\zdTestStruct2Json_desc.json");
	ofs1 << strDesc;
	ofs1.close();

	ofs2.open("d:\\zdTestStruct2Json_Json.json");
	ofs2 << strJsonOut;
	ofs2.close();

	return 0;
}

上面代碼中,用到了一些C++的宏定義,以簡化代碼的編寫,主要定義如下

/////////////////////////////////////
#define ZD_DECLARE_JSON_DESC 	\
		Json::Value stDesc; \
		Json::Value vMemb;

#define ZD_SET_JSON_DESC(KeyName, KeyType, TypeLen, _Offset) \
	vMemb[ZD_KEY] = KeyName; \
	vMemb[ZD_TYPE] = KeyType; \
	vMemb[ZD_LEN] = TypeLen; \
	vMemb[ZD_OFFSET] = _Offset; \
	stDesc.append(vMemb);
/////////////////////////////////////

以上是簡單數據結構的轉換,如果C++數據結構包含數組,轉換的例子可以參考代碼中如下名稱的函數

void testAry1D(); //一維數組轉換爲JSON
void testAry2D(); //二維數組轉換爲JSON
int zdStructObjArray2JsonDemo(const tag_ConfigColumn *st1, std::string &strJsonDesc); //對象數組轉換爲JSON用例

二),Json數據轉換爲C++數據結構

      Json到C++的轉換相對比較直接,這裏給出一段簡單的例子,下文中,pData是Json格式的字符串。
 

        //Json轉換爲C++對應數據結構

        //解析Json
        Json::Value JsonLogonMB;
        bool bPOK = JsonParse((char *)pData, wDataSize, JsonLogonMB);
        if(!bPOK) return false;


        struct tagLogon LogonBuf;
        memset(&LogonBuf, 0, sizeof(LogonBuf));
        
        LogonBuf.wID = JsonLogonMB["wID"].asUInt();
        LogonBuf.dwVersion = JsonLogonMB["dwVersion"].asUInt();
        LogonBuf.wFlags = JsonLogonMB["wFlags"].asUInt();

        //ANSI字符串轉換爲TCHAR(雙字節)字符串
        CA2CT tcsPwd(JsonLogonMB["szPassword"].asCString());
        _tcsncpy( LogonBuf.szPassword, tcsPwd.m_psz, sizeof(LogonBuf.szPassword));



 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章