基于JsonCpp, 实现C++数据结构与Json格式数据的互相转换

在一个项目中,由于客户端与服务端程序各自采用编程平台有差别,

在两者进行Socket网络通信的时候,就面临数据包格式的一致性问题。

对于这种不同平台程序间的通信问题,

当然也可以使用protobuf,thrift等成熟的工具对数据进行序列化和反序列化处理。

但是由于客户端的框架代码基于原来Json格式,服务器则是C++数据结构格式,

两者都已经存在现成的框架代码,为了不做大的变动,就在服务器端对通信收发的数据进行格式转换,

也就是需要将C++数据结构与Json格式的数据互相转换。

本文是基于JsonCpp 0.6.0-rc2 版本对Json数据进行解析的。(附JsonCpp最新版本下载链接)

一)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格式的例子

//此函数为测试用例
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++数据结构包含数组,转换的例子可以参考代码中如下名称的函数

////***包含数组的数据结构转换为JSON的用例***
int zdStructArrayItem2JsonDemo(const tag_ConfigColumn *st1, std::string &strJsonDesc)

二),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));

本文相关的完整代码已经打包上传CSDN,如需下载请点击下方链接 “JsonCpp的简易封装”

JsonCpp的简易封装

下载上述代码之后进行解压,在VC++项目中包含解压的代码,

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

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

ps:这里没有将JsonCpp编译成一个lib库,而是包含所有源代码,

如果需要用lib库的形式,请自行封装。

猜你喜欢

转载自blog.csdn.net/baodi_z/article/details/82664568