Jsoncpp 是一个用来处理 Json文本的开源C++库,最近需要分析服务器的Json格式数据,所以,学习了一下。下面就简单介绍使用Jsoncpp的Json::Reader和Json::Value来分析数据。
Json::Value操作有点像数组,先回忆下C/C++里的数组。
数组的定义是内存存储类型相同的连续的内存空间,其定义方式如下:
数组类型名 数组名[元素个数];
以上是一维数组,二维数组可看作是每个元素都是一个一维数组的一维数组,其定义如下:
数组类型名 数组名[元素分组个数][每组元素个数];
比如初始化一个一维数组:
char name[8] = "your name";
此时,name不足以容纳字符串”your name“,最后一个字符e会舍弃
而当你在name里存储三个名字时,就需要这样定义它:
char* name2[3] = {"your name","my name","his name"};
printf("%s|%s|%s\n",name2[0],name2[1],name2[2]);
输出:
your name|my name|his name
这样的话,相当于每个元素是char*,一共有三个元素。这不是一维数组,怎么说是二维的呢?
这样写就看得清楚了:
char name3[3][5] = {"name1","name2","name3"};
然后用输出
printf("%s|%s|%s\n",name3[0],name3[1],name3[2]);
结果是
name1name2name3L@|name2name3L@|name3L@
和预想的不同。为什么呢?
因为字符串是以 '\0'结尾的,所以"name1"是6个字符,把声明时的char name[3][5]改成char name[3][6]就行了:
char name3[3][6] = {"name1","name2","name3"};
printf("%s|%s|%s\n",name3[0],name3[1],name3[2]);
结果是:
name1|name2|name3
这时候,访问单个字符只需要按照下标访问就好了,比如name[1][2],访问的是"name2"里的下标为2的字符,就是'm'
问题来了,如果是按char* name[3],虽然可以访问每个元素,但是元素里的元素不方便访问,而char name[3][6]虽然每个分组和组内元素都比较好访问,但是每组元素必须长度相同,如果有一个"name22",就无法正常访问到了。这势必限制的数组在程序中的使用范围。而vector和map,queue等结构也是这样的道理,vector如果能满足所有场景,也就不会有其他结构了,增删改查论速度的话vector是很优秀的(目测这可能是研发出Json的人的一个初衷吧)。当然,以上只是猜测~~hhh~~下面谈谈Json::Value, Json::Reader.
Json是空间,Value是类名,为了方便阅读,我只留下了少部分成员:
namespace Json{
class Value{
public:
class CZString {
public:
enum DuplicationPolicy
{
noDuplication = 0,
duplicate,
duplicateOnCopy
};
CZString( int index );
CZString( const char *cstr, DuplicationPolicy allocate );
~CZString();
CZString &operator =( const CZString &other );
const char *c_str() const;
private:
const char *cstr_;
int index_;
};
typedef std::map<CZString, Value> ObjectValues;
Value( Int value );
Value( const char *value );
Value &operator[]( const char *key );
Value &operator[]( const std::string &key );
union ValueHolder
{
Int int_;
UInt uint_;
char *string_;
ObjectValues *map_;
} value_;
}//end class Value
};
namespace Json{
class Reader{
public:
bool parse( const std::string &document,Value &root,
bool collectComments = true );
bool parse( const char *beginDoc, const char *endDoc, Value &root,
bool collectComments = true );
};
};
好,这两个类先放在这里。现在假设一个情景:客户端用recv()接收服务器传回的学生信息,读取语文成绩。
recv的数据格式如下:
"{"STUDENT":[{"STUDENT":"F","When":1611893741,"Code":-1,"Msg":"18","Class":"3-1"}],"DETAIL":[{"port":8888,"timezone":8,"NAME":"ZHANG","MATH":70,"ENGLISH":65,"CHINESE":"123","AGE":19,"reconfigtimes":0}],"id":1}"
熟悉吗,这是不是和数组一样,第一个元素是STUDENT,里面每个元素是一个键值对,用逗号分开,第二个元素是DETAIL,里面也是键值对,比如第一个元素是"port":8888,以此类推。只是,这个"数组",在内存中是存储类型相同但长度不同的连续的存储空间,它最小的元素是一个可以转换任意基础或定义类型的联合 union ValueHolder。
Json::Value student = "接收的信息";
student["DETAIL"];//表示[{"port":8888,"timezone":8,"NAME":"ZHANG","MATH":70,"ENGLISH":65,"CHINESE":"123","AGE":19,"reconfigtimes":0}]
然而,想要取出学生的语文成绩123分,还需要再分析,这就需要了解一下map结构了;
typedef std::map<CZString, Value> ObjectValues;
Value类的每一个最基本的值如上所示,是类似于student["key"] = Json::Value();的结构。
好,现在开始分析啦:
第一步,用Reader接收以上信息,代码如下:
char buf[1024];
int retval = recv(socket,buf,sizeof(buf),0);
if(retval <= 0){
//error msg;
}
Json::Reader reader;
Json::Value student;
if(reader.parse(buf, student)) {
//调用reader的成员函数parse(const std::string& ag1, Value& ag2,bool ag3 = true);
//把buf中的数据解析到student中,student就可以理解成一个二维数组的名称了
//比如student["DETAIL"],就相当于上面提到的第二个元素
}
第二步,确定语文成绩所在的具体位置
if(reader.parse(buf, student)) {
//现在,detail相当于一个包含许多个map对象的一维数组
Json::Value detail = student["DETAIL"];
//如果提前知道语文成绩所在下标,就可以直接这样
//if(!detail[detail.size()-2]["CHINESE"].isNull()){
// cout << detail[detail.size()-2]["CHINESE"].asInt();
//}else{
// //err message;
//}
for(int i = 0; i < detail.size();i++){
//遍历detail,判断"CHINESE"是否存在
//这样会把每个元素都循环一遍,数据量大时性能会下降
if(!detail[i]["CHINESE"].isNULL()){
cout << detail[i]["CHINESE"].asInt();
}
}
}