近期在做一个有15个相关数据库三层架构类的项目,要运用到遍历15个类中特定成员的名称和值,把它们转为JSON字符串;同时可以通过输入JSON字符串修改类中特定成员的值。下面是三种解决方案:
0.想在网上找一个这样的库来实现(可惜没找着)
1.笨而可靠的办法:为每个类写一个转为JSON字符串函数,再写一个JSON字符串转实体类的函数;(笨是笨了点,但是灰常可靠)
2.为15个类写一个Base类,让转JSON字符串函数和JSON字符串转实体类的函数在Base类中实现,同时争取在15个类中尽量少做修改;
下面对方案2的俩函数进行思路扩展(按照俺已经实现的思路哈,其他分叉思路就略过不提了,回忆太费脑细胞了):
有类,比如:
Typedef long long BigInt;
class A
{
public:
TestCls1(void);
~TestCls1(void);
Public(private):
byte* c;
char * i;
std::string m_str1;
std::string m_str2;
// int LL;
// long lint;
BigInt bint;
Private:
std::string m_str;
};
1.转JSON字符串函数的思路(以类A为例):
JSON字符串例子:
std::string_jsonStr="{\"c\":\"hehe\",\"m_str1\":\"123456789123456789\",\"m_str2\":\"000\",\"bint\":123456789123456789, \"i\":\"abc\"}"
JSON字符串中的内容分别对应类A中特定成员的变量名称和值;
首先,遍历范围控制---在15个类中添加一对约束宏JSON_START和JSON_END来控制范围;如下:
class A
{
public:
TestCls1(void);
~TestCls1(void);
Public(private):
JSON_START;//开始遍历的宏
byte* c;
char * i;
std::string m_str1;
std::string m_str2;
std::string m_str4;
int LL;
long lint;
BigInt bint;
JSON_END;//结束遍历的宏
Private:
std::string m_str;
};
JSON_START和JSON_END声明在哪里呢?很显然要在Base父类中
然后,如何获取范围中的变量名称-----通过浏览和判断头文件的方式来取得约束宏JSON_START和JSON_END中的变量名称
那么接下来的问题,如何获取15个不同类的头文件呢?答案:__FILE__;最好定义在JSON_END这个宏中,既然声明了一个宏,白用白不用啦(也可以在JSON_START中)
如何获取变量名称呢?这个比较简单啦,打开头文件,行获取文本内容,字符串查找啦,合理利用空格啦等等只要实现就好,最好把该成员的类型名也获取到,因为后面要用到(俺那个BigInt类型就是利用空格,最后对于具有诸如unsiged char/long long 两个单词类型说明不适用所规定的,最好把算法做完善)。
接下来,如何获取值-----如果你能通过实体类的首地址来访问和修改类中所有成员的值的话,那么接下来的思路不说你也知道啦;但是如果你还不知道这样的方式,那么请认真阅读下面的代码:
#include <string>
#include <iostream>
using namespace std;
#define STR_ADVANCESIZE 16 //string给字符串预留的内存大小
int _tmain(int argc, _TCHAR* argv[])
{
int strAddrToMem_LengthAddr =0; //string首地距其成员长度的地址差
int strAddrToMem_C_strAddr = 0; //string首地距其成员内容的地址差
std::string testStr1 = "12345"; //这个string长度小于16
std::string testStr2 =
"1234567891234567"; //这个string长度大于等于16
//首先,获取string字符串长度距离首地址的差值
for(int i=0; i<sizeof(std::string); i++)
{
if(*(int*)((int)&testStr1+i) ==
testStr1.length()) //获取strAddrToMem_LengthAddr
{
strAddrToMem_LengthAddr = i;
break;
}
}
cout<<*(int*)((int)&testStr1+strAddrToMem_LengthAddr)
<<" "<<testStr1.length()<<endl; //看看这俩值是不是一样的?
*(int*)((int)&testStr1+
strAddrToMem_LengthAddr) =
10; //修改一下
cout<<*(int*)((int)&testStr1+strAddrToMem_LengthAddr)
<<" "<<testStr1.length()<<endl; //看看testStr1.length()修改了吗?
//然后获取string中字符串内容距离首地址的差值
for(int i=0; i<sizeof(std::string); i++)
{
if(!strcmp((char*)((int)&testStr1 + i),
testStr1.c_str())) //长度小于的testStr1比较一下
{
strAddrToMem_C_strAddr = i;
break;
}
}
cout<<(char*)((int)&testStr1 + strAddrToMem_C_strAddr)
<<endl; //看见什么了吗?
memcpy((char*)((int)&testStr1 + strAddrToMem_C_strAddr),
"haha", 5); //修改一下
cout<<testStr1.c_str()<<endl; //又看见什么了吗?
//那长度大于string预分配的大小呢?看下面就明白啦
cout<<(char*)(*(int*)((int)&testStr2 + strAddrToMem_C_strAddr))
<<endl; //希望你能明白^_^
return 0;
}
现在已经知道,可以通过实体类首地址来访问和修改成员的值;但是我们不可能知道我们要遍历的特定成员在类中哪个位置啊?所以不能利用类首地址。但是以此为借鉴,我们就在类中自定义一个我们自己的变量int json_begin,并将其添加到开始宏JSON_START中,如下:
#define JSON_START int\
json_begin;\
这下,子类中就安插了一个获取需要遍历成员堆栈中的最小地址& json_begin,然后利用类型(前面浏览头文件的时候不是叫最好获取吗?)所占字节数,以此递增,可获得接下来的成员的地址及值,直至结束宏。该实现在继承Base类时需要传入& json_begin的值到Base类的构造函数中;作为一个已知量,然后在Base类中实现访问和修改子类中的成员。
2.json字符串修改实体类中成员的值函数思路扩展:
其实嘛,和上一步是相反的,所以不再解释, 嘿嘿~~~~;
要注意几点:string类空间大小的判断,指针的判断和释放,指针的内存分配;
再再接下来,看看俺自己的代码及效果(Base类省去,太多啦):
1. 先声明一个子类,继承Base类,添加宏JSON_START和JSON_END(TestCls.h)
#pragma once
#include "CTranverseBase.h"
#include <iostream>
using namespace std;
namespace TESTCLS
{
class TestCls: public CTranverseBase
{
public:
TestCls(void);
~TestCls(void);
public:
JSON_START;
byte* c;
char * i;
std::string m_str1;
std::string m_str2;
BigInt bint;
JSON_END;
private:
int j;
std::string m_str3;
std::string m_str;
};
}
2. 在main函数中的调用:
#include "CTranverseBase.h"
#include "TestCls.h"
#include <string>
void TestStringCls();
int _tmain(int argc, _TCHAR* argv[])
{
std::string jsonStr = "{\"c\":\"hehe\",\"m_str1\":\"123456789123456789\",\"m_str2\":\"000\",\"bint\":123456789123456789, \"i\":\"abc\"}";
TestCls mmm;
CTranverseBase *baseC = &mmm;
cout<<baseC->MemDataToJsonStr()<<endl;//转json字符串
baseC->JsonStrToMemValue(jsonStr);// json字符串修改实体类成员
cout<<baseC->MemDataToJsonStr()<<endl;//转json字符串,再次打印看修改没
return 0;
}
可以实现不同类的成员同JSON之间的转换:
继续添加另外一个类,同样继承Base类:
#pragma once
#include "../TraverseClassByMemory/CTranverseBase.h"
class TestCls1:public CTranverseBase
{
public:
TestCls1(void);
~TestCls1(void);
public:
JSON_START;
std::string m_str5;
int __int;
std::string m_str1;
int LL;
long lint;
BigInt bint;
std::string xxxxstr2;
std::string exstr3;
JSON_END;
int otherArg;
private:
//int m;
int j;
std::string m_str4;
std::string m_str;
};
2.在main.cpp中添加调用新类的代码和测试代码:(调用就一俩句,测试的话就多啦,要看效果嘛)
#include "stdafx.h"
#include "../TraverseClassByMemory/CTranverseBase.h"
#include "TestCls.h"
#include "TestCls1.h"
#include <iostream>
#include <string>
using namespace std;
void TestStringCls();
int _tmain(int argc, _TCHAR* argv[])
{
//TestStringCls();
std::string jsonStr =
"{\"c\":\"hehe\",\"m_str1\":\"123456789123456789\",\"m_str2\":\"000\",\
\"bint\":123456789123456789, \"i\":\"abc\",\"m_str3\":\"ddd\",\"m_str5\":\
\"llllllllllll\"}";
std::string jsonStr1 =
"{\"lint\":202,\"m_str1\":\"123456789123456789\",\"xxxxstr2\":\"000\",\
\"bint\":123456789123456789, \"__int\":101,\"exstr3\":\"ddd\",\"m_str5\":\
\"llllllllllll\",\"LL\":111222}";
CTestCls mmm;
TestCls1 kkk;
CTranverseBase *baseC = &mmm;
CTranverseBase *baseC1 = &kkk;
cout<<"子类的特定成员名称和初始化值转为JSON字符串:\n\t"<<baseC->MemDataToJsonStr()<<endl;
baseC->JsonStrToMemValue(jsonStr);
cout<<"通过jsonStr修改子类的特定成员值后转为JSON字符串:\n\t"<<baseC->MemDataToJsonStr()<<endl;
cout<<"mmm类中成员的当前值:\n\tmmm.c = "
<<mmm.c<<"\n\tmmm.i = "
<<mmm.i<<"\n\tmmm.m_str1 = "
<<mmm.m_str1.c_str()<<"\n\tmmm.m_str2 = "
<<mmm.m_str2.c_str()<<"\n\tmmm.bint = "
<<mmm.bint<<endl;
cout<<"\tmmm.m_str3 = "<<mmm.m_str3.c_str()<<endl;
cout<<"\tmmm.m_str5 = "<<mmm.m_str5.c_str()<<endl;
cout<<"子类的特定成员名称和初始化值转为JSON字符串:\n\t"<<baseC1->MemDataToJsonStr()<<endl;
baseC1->JsonStrToMemValue(jsonStr1);
cout<<"通过jsonStr修改子类的特定成员值后转为JSON字符串:\n\t"<<baseC1->MemDataToJsonStr()<<endl;
cout<<"kkk类中成员的当前值:"<<endl;
cout<<"\tkkk.__int = "<<kkk.__int<<endl;
cout<<"\tkkk.LL = "<<kkk.LL<<endl;
cout<<"\tkkk.m_str1 = "<<kkk.m_str1.c_str()<<endl;
cout<<"\tkkk.m_str5 = "<<kkk.m_str5<<endl;
cout<<"\tkkk.xxxxstr2 = "<<kkk.xxxxstr2.c_str()<<endl;
cout<<"\tkkk.exstr3 = "<<kkk.exstr3.c_str()<<endl;
cout<<"\tkkk.bint = "<<kkk.bint<<endl;
cout<<"\tkkk.lint = "<<kkk.lint<<endl;
return 0;
}
最后,这是一个由于自己兴趣写的代码;博文是临时发表,难免出错,还请阅者海涵。如果你急需这样的功能来实现你自己的项目,可以联系俺,俺会无偿发给你;但是不包没bug(一般按照俺给的头文件上的要求来做的话,都不会出现bug,也有例外) 嘎嘎。