什么是序列化和反序列化?
把应用层的对象转换成一段连续的二进制串,或者反过来,把二进制串转换成应用层的对象–这两个功能就是序列化和反序列化。
序列化:把对象转换为字节序列的过程称为对象的序列化。
反序列化:把字节序列恢复为对象的过程称为对象的反序列化
数据结构、对象与二进制串
不同的计算机语言中,数据结构,对象以及二进制串的表示方式并不相同。
数据结构和对象:对于类似Java这种完全面向对象的语言,工程师所操作的一切都是对象(Object),来自于类的实例化。在Java语言中最接近数据结构的概念,就是POJO(Plain Old Java Object)或者Javabean--那些只有setter/getter方法的类。而在C++这种半面向对象的语言中,数据结构和struct对应,对象和class对应。
二进制串:序列化所生成的二进制串指的是存储在内存中的一块数据。C++语言具有内存操作符,所以二进制串的概念容易理解,例如,C++语言的字符串可以直接被传输层使用,因为其本质上就是以’\0’结尾的存储在内存中的二进制串(Object)。对于跨语言间的通讯,序列化后的数据当然不能是某种语言的特殊数据类型。
如果不使用序列化,直接在发送端和接收端发送对象会产生什么问题?
客户端向服务器端发送一个结构体,对象如下:
struct Data {
std::string name;
std::string school;
std::string msg;
std::string cmd;
}
Data d;
d.name="小红";
d.school="希望小学";
d.msg="hello";
d.cmd=" ";
Writen(sockfd,&d,sizeof(d));
服务器端代码
Data d;
Read(sockfd,&d,sizeof(d))
这样写会有什么问题呢?其实以上代码存在三个潜在的问题:
(1)不同的实现以不同的格式存储二进制数。最常见的便是不同的字节序问题。有的机器使用大端字节序有的机器使用小端字节序。
(2)不同的实现在存储相同的C数据类型上可能存在差异。举例来说,大多数32位unix系统使用32位表示长整数,而64位系统却典型地使用64位来表示同样的数据类型。对于short、int或long等整数类型,它们各自的大小没有确定的保证。
(3)不同的实现给结构体打包的方式存在差异,取决于各种数据类型所用的位数以及机器的对齐限制。
因此,穿越套接字传送二进制结构绝不是明智的。
常用的序列化与反序列化协议
当下比较流行的序列化协议,包括XML、JSON、Protobuf、Thrift和Avro。
Json
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。 易于人阅读和编写。同时也易于机器解析和生成。 它基于JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一个子集。 JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C, C++, C#, Java, JavaScript, Perl, Python等)。 这些特性使JSON成为理想的数据交换语言。
JSON建构于两种结构:
“名称/值”对的集合(A collection of name/value pairs)。不同的语言中,它被理解为对象(object),纪录(record),结构(struct),字典(dictionary),哈希表(hash table),有键列表(keyed list),或者关联数组 (associative array)。
值的有序列表(An ordered list of values)。在大部分语言中,它被理解为数组(array)。
这些都是常见的数据结构。事实上大部分现代计算机语言都以某种形式支持它们。这使得一种数据格式在同样基于这些结构的编程语言之间交换成为可能。
JSON具有以下这些形式:
对象是一个无序的“‘名称/值’对”集合。一个对象以“{”(左括号)开始,“}”(右括号)结束。每个“名称”后跟一个“:”(冒号);“‘名称/值’ 对”之间使用“,”(逗号)分隔。
{
"name" : "tocy",
"age" : 1000
}
数组是值(value)的有序集合。一个数组以“[”(左中括号)开始,“]”(右中括号)结束。值之间使用“,”(逗号)分隔。
[{"name":"tocy"}, {"age":1000}, {"domain":"cn"}]
值(value)可以是双引号括起来的字符串(string)、数值(number)、true、false、 null、对象(object)或者数组(array)。这些结构可以嵌套。
字符串(string)是由双引号包围的任意数量Unicode字符的集合,使用反斜线转义。一个字符(character)即一个单独的字符串(character string)。
字符串(string)与C或者Java的字符串非常相似。
数值(number)也与C或者Java的数值非常相似。除去未曾使用的八进制与十六进制格式。除去一些编码细节。
jsconcpp
C++要使用JSON来解析数据,一般采用jsoncpp。
jsoncpp中主要的类:
Json::Value:可以表示所有支持的类型,如:int , double ,string , object, array等。其包含节点的类型判断(isNull,isBool,isInt,isArray,isMember,isValidIndex等),类型获取(type),类型转换(asInt,asString等),节点获取(get,[]),节点比较(重载<,<=,>,>=,==,!=),节点操作(compare,swap,removeMember,removeindex,append等)等函数。
Json::Reader:将文件流或字符串创解析到Json::Value中,主要使用parse函数。Json::Reader的构造函数还允许用户使用特性Features来自定义Json的严格等级。
Json::Writer:与JsonReader相反,将Json::Value转换成字符串流等,Writer类是一个纯虚类,并不能直接使用。在此我们使用 Json::Writer 的子类:Json::FastWriter(将数据写入一行,没有格式),Json::StyledWriter(按json格式化输出,易于阅读)。
Json::Reader可以通过对Json源目标进行解析,得到一个解析好了的Json::Value,通常字符串或者文件输入流可以作为源目标。
序列化:
void Serialize(std::string* output) {
Json::Value value;
value["name"] = name;
value["school"] = school;
value["msg"] = msg;
value["cmd"] = cmd;
Json::FastWriter writer;
*output = writer.write(value);
return;
}
反序列化:
void UnSerialize(const std::string& input) {
Json::Value value;
Json::Reader reader;
reader.parse(input, value);
name = value["name"].asString();
school = value["school"].asString();
msg = value["msg"].asString();
cmd = value["cmd"].asString();
return;
}
json优点及缺点:灵活、纯文本数据 方便阅读可读性、扩展性好,占用空间较大浪费