在一个项目中,由于客户端与服务端程序各自采用编程平台有差别,
在两者进行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));