当前位置: 首页 > 工具软件 > JSONVALUE > 使用案例 >

如何用Json::Value和Json::Reader分析Json格式的数据

晁文斌
2023-12-01

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<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
};

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

 

 

 

 

 

 类似资料: