【Linux】序列化和反序列化

文章目录

  • 定义
  • 利用 Json 实现序列化反序列化
    • Json 的认识
    • Jsoncpp 库的下载与认识
    • 实现序列化
    • 实现反序列化

在网络编程中,直接使用 结构体 进行数据传输会出错,因为本质上socket无法传输结构体,我们只有将结构体装换为字节数组,或者是字符串格式来传输,然后对端主机收到了数据,再将其转化为结构体,这就是序列化和反序列化的过程!

定义

序列化 (Serialization)是将对象的状态信息转换为可以存储传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。

序列化主要有两个用途:

  • 把对象的字节序列永久保存到硬盘上,通常存放在一个文件中(序列化对象)
  • 网络上传送对象的字节序列(网络传输对象)

实际上就是将数据持久化,防止一直存储在内存当中,消耗内存资源。而且序列化后也能更好的便于网络运输何传播。

如下,在网上找到的一张图片,可以很好地描述序列化和反序列化,及其作用(以 java 对象为例)。
【Linux】序列化和反序列化_第1张图片

利用 Json 实现序列化反序列化

Json 的认识

json 是一种数据交换格式,采用完全独立于编程语言的文本格式来存储和表示数据。

例如:小明同学的学生信息:

char name = "小明";
int age = 18;
float score[3] = {88.5, 99, 58};

则json这种数据交换格式是将这多种数据对象组织成为一个字符串:

[
	{
		"姓名" : "小明",
		"年龄" : 18,
		"成绩" : [88.5, 99, 58]
	},
	{
		"姓名" : "小黑",
		"年龄" : 18,
		"成绩" : [88.5, 99, 58]
	}
]

json 数据类型:对象,数组,字符串,数字。

  • 对象:使用花括号 {} 括起来的表示一个对象。
  • 数组:使用中括号 [] 括起来的表示一个数组。
  • 字符串:使用常规双引号 “” 括起来的表示一个字符串
  • 数字:包括整形和浮点型,直接使用。

Jsoncpp 库的下载与认识

sudo yum install epel-release
sudo yum install jsoncpp-devel

依次执行上面两个指令,然后在 /usr/include/jsoncpp/json/ 目录下查看是否有相关文件,如下图,有则下载成功。

在这里插入图片描述

jsoncpp 库用于实现 json 格式的序列化和反序列化,完成将多个数据对象组织成为 json 格式字符串,以及将 json格式字符串解析得到多个数据对象的功能。

这其中主要借助三个类以及其对应的少量成员函数完成:

在这里过多介绍下面接口肯略微抽象,要在下面的使用过程中去理解其用法。

//Json数据对象类
class Json::Value {
	Value &operator=(const Value &other); //Value重载了[]和=,因此所有的赋值和获取数据都可以通过
	Value& operator[](const std::string& key);//简单的方式完成 val["姓名"] = "小明";
	Value& operator[](const char* key);
	Value removeMember(const char* key);//移除元素
	const Value& operator[](ArrayIndex index) const; //val["成绩"][0]
	Value& append(const Value& value);//添加数组元素val["成绩"].append(88);
	ArrayIndex size() const;//获取数组元素个数 val["成绩"].size();
	std::string asString() const;//转string string name = val["name"].asString();
	const char* asCString() const;//转char* char *name = val["name"].asCString();
	Int asInt() const;//转int int age = val["age"].asInt();
	float asFloat() const;//转float
	bool asBool() const;//转 bool
};


//json序列化类,低版本用这个更简单
class JSON_API Writer {
	virtual std::string write(const Value& root) = 0;
}
class JSON_API FastWriter : public Writer {
	virtual std::string write(const Value& root);
}
class JSON_API StyledWriter : public Writer {
	virtual std::string write(const Value& root);
}
//json序列化类,高版本推荐,如果用低版本的接口可能会有警告
class JSON_API StreamWriter {
	virtual int write(Value const& root, std::ostream* sout) = 0;
}
class JSON_API StreamWriterBuilder : public StreamWriter::Factory {
	virtual StreamWriter* newStreamWriter() const;
}



//json反序列化类,低版本用起来更简单
class JSON_API Reader {
	bool parse(const std::string& document, Value& root, bool collectComments = true);
}
//json反序列化类,高版本更推荐
class JSON_API CharReader {
	virtual bool parse(char const* beginDoc, char const* endDoc,
	                   Value* root, std::string* errs) = 0;
}
class JSON_API CharReaderBuilder : public CharReader::Factory {
	virtual CharReader* newCharReader() const;
}

实现序列化

#include    
#include    
#include    
#include    
    
using namespace std;    
    
int main()    
{    
  const char *name = "小明";    
  int age = 18;    
  float score[] = {88.5, 98, 58};    
  Json::Value val;    
  // 在val对象中写入数据
  val["姓名"] = name;    
  val["年龄"] = age;    
  val["成绩"].append(score[0]);  // 因为 score 是数组,所以存入Json::Value 对象要用 append 
  val["成绩"].append(score[1]);    
  val["成绩"].append(score[2]);    
  
  Json::StreamWriterBuilder swb;    
  std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());    
  std::ostringstream os;    
  sw->write(val, &os);  // 将 val对象序列化,并放到 os 中  
  std::string str = os.str();                                                                                                    
  std::cout << str <<std::endl;  // 打印序列化之后的内容
  return 0;    
}   

如上代码,有 val 对象,里面存储了一些数据(姓名、年龄、成绩),然后将其序列化,打印序列化之后的内容,结果如下。

序列化的过程我们无需关心,因为这是 StreamWriter 封装好的,我们只需要调用其 write 接口,传入参数即可。

【Linux】序列化和反序列化_第2张图片
此时,这些序列化之后的内容,就可以在网络中传输,也就是说,如果要进行网络通信传输这些数据,就可以调用 write/sendto/send 等等接口,将上面代码中的 str 进行传输!而不是直接传输 val 对象!

实现反序列化

#include    
#include    
#include    
#include    
    
using namespace std;    
    
int main()    
{    
  std::string str = R"({"姓名":"小明", "年龄":18, "成绩":[76.5, 55, 88]})";    
  Json::Value root;    
  Json::CharReaderBuilder crb;    
  std::unique_ptr<Json::CharReader> cr(crb.newCharReader());    
  std::string err;    
  cr->parse(str.c_str(), str.c_str() + str.size(), &root, &err);   // 反序列化 
  
  std::cout << root["姓名"].asString() << std::endl;    
  std::cout << root["年龄"].asInt() << std::endl;    
  int sz = root["成绩"].size();    
  
  for (int i = 0; i < sz; i++) {                                                                                                 
    std::cout << root["成绩"][i].asFloat() << std::endl;    
  }    
  // 第二种遍历方式
  for (auto it = root["成绩"].begin(); it != root["成绩"].end(); it++){    
    std::cout << it->asFloat() << std::endl;    
  }    
  return 0;    
}  

如上,现在有一段序列化之后的数据 str,里面的内容是完全按照 json 的格式来组织的。现在要完成反序列化,将其数据放入 root 对象中。

本质上只要调用 CharReader 的 parse 接口,依次传参即可,然后 root 中就会被放入数据。

【Linux】序列化和反序列化_第3张图片

当然,实现序列化和反序列化不是只有用 jsoncpp 库这一个办法,还有其他办法例如:protbuf 等等。

你可能感兴趣的:(Linux,linux,服务器,网络编程)