Json介绍和使用

398 阅读4分钟

一. Json介绍

Json是一种轻量级的数据交换格式(也叫数据序列化格式)。Json采用完全独立于编程语言的文本格式来存储和表示数据,简洁和清晰的结构层次使Json成为理想的交换语言。易于人阅读和编写,同时也易于机器的解析和生成,并有效的提升网络传输效率。

本项目消息的种类有很多,可能会分为登录消息、注册消息、聊天消息、加好友还是创建群组的消息,比如2是聊天消息,里面应该包含消息来自谁(from)、给谁说的(to)和具体的消息内容(msg),我们通过tcp发送消息发送的都是字节流,所以对于这种消息结构我们应该进行数据序列化转成相应的字节流去发送,发送到远端以后,远端接收到字节流需要上报给应用,应用再从字节流里把数据反序列化。

msg_type:2
from:xxx
to:xxx
msg:xxx

image.png

二、一个优秀的Json三方库

JSON for Modern C++ 是一个由德国大牛 nlohmann 编写的在 C++ 下使用的 JSON 库,具有以下特点:

  • 直观的语法
  • 整个代码由一个头文件组成 json.hpp,没有子项目,没有依赖关系,没有复杂的构建系统,使用起来非常方便
  • 使用 C++ 11 标准编写
  • 使用 json 像使用 STL 容器一样
  • STL和 json 容器之间可以相互转换
  • 严谨的测试:所有类都经过严格的单元测试,覆盖了 100% 的代码,包括所有特殊的行为。此外,还检查了 Valgrind 是否有内存泄漏。为了保持高质量,该项目遵循核心基础设施倡议(CII)的最佳实践

三、Json序列化和反序列化

1. 包含json头文件

在网络中,常用的数据传输序列化格式有XML,Json,ProtoBuf,在公司级别的项目中,大量的在使用ProtoBuf作为数据序列化方式,以其数据压缩编码传输,占用带宽小,同样的数据信息,是Json的1/10,XML的1/20,但是用起来比Json稍微复杂一些,所以项目中我们选用Json格式来打包传输数据。

JSON for Modern C++这个三方库的使用非常简单,如下所示:

#include "json.hpp
using json = nlohmann::json;

2. Json的序列化

就是把我们想要打包的数据,或者对象,直接处理成Json字符串

  • 普通数据序列化
#include "json.hpp"
using json = nlohmann::json;

#include <iostream>
#include <vector>
#include<map>
using namespace std;

//json序列化示例1
void func1(){
    json js;
    js["msg_type"] = 2;
    js["from"] = "zhang san";
    js["to"] = "li si";
    js["msg"] = "hello, what are you doing now?";
    cout << js << endl;
}

int main(){
    func1();
    return 0;
}

image.png 可以理解为底层是链式哈希表,有键,有值,但是是无序的!内部存储先后顺序和插入顺序无关。 json之所以能输出是因为json对象提供了输出运算符的重载函数,现在要把json对象通过网络发送出去就需要转成字符串类型

void func1(){
    json js;
    js["msg_type"] = 2;
    js["from"] = "zhang san";
    js["to"] = "li si";
    js["msg"] = "hello, what are you doing now?";

    //把js中的数据序列化后的字符串赋给sendBuf
    string sendBuf = js.dump();
    const char* str = sendBuf.c_str();
    cout << str << endl;
}

json的键可以是整数类型,字符串类型,还可以是数组类型

#include "json.hpp"
using json = nlohmann::json;

#include <iostream>
#include <vector>
#include<map>
using namespace std;

void func2(){
    json js;
    //添加数组
    js["id"] = {1,2,3,4,5};
    //添加key - value
    js["name"] = "zhang san";
    //添加对象
    js["msg"]["zhang san"] = "hello world";
    js["msg"]["liu shuo"] = "hello china";
    //上面等同于下面这句一次性添加数组对象
    js["msg"] = {{"zhang san", " hello world"}, {"liu shuo", "hello china"}};
    cout << js << endl;
}
int main(){
    func2();
    return 0;
}

image.png

  • 容器序列化
#include "json.hpp"
using json = nlohmann::json;

#include <iostream>
#include <vector>
#include<map>
using namespace std;

void func3(){
    json js;
    vector<int> vec;
    vec.push_back(1);
    vec.push_back(2);
    vec.push_back(3);
    
    //直接序列化一个vector容器
    js["list"] = vec;

    map<int, string> m;
    m.insert({1, "黄山"});
    m.insert({2, "华山"});
    m.insert({3, "泰山"});
    
    //直接序列化一个map容器
    js["path"] = m;

    string sendBuf = js.dump(); //json数据对象 =》序列化 json字符串
    cout << sendBuf.c_str() <<endl;
}
int main(){
    func3();
    return 0;
}

image.png

3. json反序列化

当从网络接收字符串为Json格式,可以用Json for Modern C++ 直接反序列化取得数据或者直接反序列化出对象,甚至容器,强大无比!

  • 字符串和整型
#include "json.hpp"
using json = nlohmann::json;

#include <iostream>
#include <vector>
#include<map>
using namespace std;

string func1(){
    json js;
    js["msg_type"] = 2;
    js["from"] = "zhang san";
    js["to"] = "li si";
    js["msg"] = "hello, what are you doing now?";

    string sendBuf = js.dump();
    return sendBuf;
}

int main(){
    // recvBuf是从网络上接收的字符串
    string recvBuf = func1();
    //json字符串 =》 反序列化成数据对象(看做容器,方便访问)
    json jsbuf = json::parse(recvBuf);
    cout << jsbuf["msg_type"] << endl;
    cout << jsbuf["from"] << endl;
    cout << jsbuf["to"] << endl;
    cout << jsbuf["msg"] << endl;
    return 0;
}

image.png

json对象里的数据会保留数据类型,序列化前是什么类型,反序列化出来就是什么类型。序列化前是char、int或map,反序列化出来就是char、int或map

  • 容器类型

#include "json.hpp"
using json = nlohmann::json;

#include <iostream>
#include <vector>
#include<map>
using namespace std;

string func2(){
    json js;
    //添加数组
    js["id"] = {1,2,3,4,5};
    //添加key - value
    js["name"] = "zhang san";
    //添加对象
    js["msg"]["zhang san"] = "hello world";
    js["msg"]["liu shuo"] = "hello china";
    //上面等同于下面这句一次性添加数组对象
    js["msg"] = {{"zhang san", " hello world"}, {"liu shuo", "hello china"}};
    return js.dump();
}

int main(){
    // recvBuf是从网络上接收的字符串
    string recvBuf = func2();
    //json字符串 =》 反序列化成数据对象(看做容器,方便访问)
    json jsbuf = json::parse(recvBuf);
    // js["id"]序列化前数组类型,反序列化出来就是数组类型
    cout << jsbuf["id"] << endl;
    // 数组可以使用下标访问
    cout << jsbuf["id"][1] << endl;

    // js["msg"]依然是Json对象类型,我们可以使用auto接收
    cout << jsbuf["msg"] << endl;

    auto msg = jsbuf["msg"];
    cout << msg["zhang san"] << endl;
    cout << msg["li si"] << endl;
    
    return 0;
}

image.png

  • vector类型和map类型
#include "json.hpp"
using json = nlohmann::json;

#include <iostream>
#include <vector>
#include<map>
using namespace std;

string func3(){
    json js;
    vector<int> vec;
    vec.push_back(1);
    vec.push_back(2);
    vec.push_back(3);

    js["list"] = vec;

    map<int, string> m;
    m.insert({1, "黄山"});
    m.insert({2, "华山"});
    m.insert({3, "泰山"});

    js["path"] = m;
    
    string sendBuf = js.dump();//json数据对象 =》序列化 json字符串
    return sendBuf;
}
int main(){
    // recvBuf是从网络上接收的字符串
    string recvBuf = func3();
    //json字符串 =》 反序列化成数据对象(看做容器,方便访问)
    json jsbuf = json::parse(recvBuf);
    vector<int> vec = jsbuf["list"]; //js对象里面的数组类型,直接放入vector容器当中
    for(int &v : vec){
        cout << v << " ";
    }
    cout << endl;

    map<int, string> mymap = jsbuf["path"];
    for(auto &p : mymap){
        cout << p.first << " " << p.second << endl;
    }
    return 0;
}

image.png