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
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 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
};
json::reader
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
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();
}
}
}