序列化与反序列化

序列化和反序列化概念

序列化是将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。简单来说,就是把内存中的数据对象按照一定的规则变成一个线性的字节序列 ,这样就方便存储到文件、数据库或者在网络上传输。

在网络通信中,数据并不是简单的把数据"揉合"在一起,然后再发送出去。而是将数据按照一定的方式组合起来,才能让接收方进行合理的解析。假如有以下数据。

新年快乐2025.1.10Jack

按照我们人类的思考方式,这个数据是叫一个Jack的用户在2025.1.10发送了一个消息,叫新年快乐,这是很容易理解的。但是对于计算机来说,这段数据可能是叫新年快乐的用户在2025.1.10发送了Jack的消息,或者是其他形式的消息组合。所以对于计算机来说,这段数据之间应该如何进行区分,分节符在哪里?

对于上面的问题,我们要设定一些规则来组织数据,这种行为被称为序列化。而从组织好的数据中还原出目标信息,称为反序列化。反序列化是序列化的逆过程,它将序列化后的字节流、XML、JSON 等格式的数据重新转换为原来的数据结构或对象的过程。这样就可以在程序中再次使用这些数据,就好像它们从未离开过内存一样。

当前主流的序列化方式有三种,分别是是XML,JSON,protobuf。接下来我们将重点学习JSON的序列化方式,如果想用二进制的方式来序列化数据,可以使用protobuf,不过学习起来会比较难。我会在后面进行protobuf的讲解。

jsoncpp的使用

jsoncpp库中的类被定义到了一个Json命名空间中,建议在使用这个库的时候先声明这个命名空间。

cpp 复制代码
using namespace Json;

使用jsoncpp库解析json格式的数据,我们只需要掌握三个类:

  • Value类:将json支持的数据类型进行了包装,最终得到一个Value类型。
  • FastWriter类:将Value对象中的数据序列化为字符串。
  • Reader类:反序列化,将json字符串解析成Value类型。

Value类

这个类可以看做是一个包装器,它可以封装Json支持的所有类型,这样我们在处理数据的时候就方便多了。

枚举类型 说明 翻译
nullValue 'null value' 不表示任何数据,空值
intValue signed integer value 表示有符号整数
uintValue usigned integer value 表示无符号整数
realValue double value 表示浮点数
stringValue UTF-8 string value 表示utf8格式的字符串
booleanValue bool value 表示布尔数
arrayValue array value(ordered list) 表示数组,即Json串中的[]
objectValue object value(collection of name/value pairs) 表示键值对,即Json串中的{}
构造函数

Value类为我们提供了很多构造函数,通过构造函数来封装数据,最终得到一个统一的类型。

cpp 复制代码
Value(ValueType type = nullValue);
Value(Int value);
Value(UInt value);
Value(Int64 value);
Value(double value);
Value(const char* value);
Value(const char* begin, const char* end);
Value(bool value);
Value(const Value& other);
Value(Value&& other);
检测保存的数据类型
cpp 复制代码
bool isNull() const;
bool isBool() const;
bool isInt() const;
bool isInt64() const;
bool isUInt() const;
bool isUInt64() const;
bool isIntegral() const;
bool isDouble() const;
bool isNumberic() const;
bool isString() const;
bool isArray() const;
bool isObject() const;

其中这里有两个接口比较特殊,一个是 isNumeric() 另一个是 isIntegral()

先对 isNumeric() 进行说明,字面意思就是"是否为数字",实际上在 Json::Value 类的实现中等同于 isDouble(),因此这两个函数是等效的,实现代码如下:

cpp 复制代码
bool Value::isNumeric() const {
   return isDouble(); }

在使用过程中可以直接用 isDouble() 代替 isNumeric(),反之亦然。
isIntegral() 的作用主要是对浮点型数据的值进行了严格限制,如果类型为 int 或者 uint,该接口直接返回真值。如果类型为 double,因为 m a x U I n t 64 = 2 64 − 1 maxUInt64=2^{64}-1 maxUInt64=264−1 不能精确表示为 double,因此 double(maxUInt64)将四舍五入为 2 64 2^{64} 264。因此,我们要求该值严格小于限制。

将Value对象转换为实际类型
cpp 复制代码
Int asInt() const;
UInt asUInt() const;
Int64 asInt64() const;
UInt64 asUInt64() const;
LargestInt asLargestInt() const;
LargestUInt asLargestUInt() const;
JSONCPP_STRING asString() const;  //返回C++风格的字符串
float asFloat() const;
double asDouble() const;
bool asBool() const;
const char* asCString() const;  //返回C分格的字符串
对json数组的操作
cpp 复制代码
ArrayIndex size() const;
Value& operator[](ArrayIndex index);
Value& operator[](int index);
const Value& operator[](ArrayIndex index) const;
const Value& operator[](int index) const;
// 根据下标的index返回这个位置的value值
// 如果没找到这个index对应的value,这回第二个参数的defaultValue
Value get(ArrayIndex index,const Value& defaultValue) const;
Value& append(const Value& value);
const_iterator begin() const;
const_iterator end() const;
iterator begin() const;
iterator end();
对json对象的操作
cpp 复制代码
Value& operator[](const char* key);
const Value& opertaor[](const char* key) const;
Value& operator[](const JSONCPP_STRING key);
const Value& operator[](const JSON_STRING& key) const;
Value& operator[](const StaticString& key);

// 通过key,得到value的值
Value get(const char* key, const Value& defaultValue) const;
Value get(const JSONCPP_STRING& key, const Value& defaultValue) const;
Value get(const CppTL::ConstString& key, const Value& defaultValue) const;

// 得到对象中所有的键值
typedef std::vector<std::string> Members;
Members getMemberNames() const;
将Value对象数据序列化为string
cpp 复制代码
// 序列化得到的字符串有样式 -> 带换行 -> 方便阅读
// 写配置文件的时候
std::string toStyledString() const;

FastWriter类

cpp 复制代码
// 将数据序列化 -> 换行
// 进行数据的网络传输
std::string Json::FastWriter::write(const Value& root);

Reader类

当需要进行反序列化的时候,就需要Reader类,核心函数接口如下:
bool Json::Reader::parse(const string& document, Value& root, bool collectComments = true);

参数:

  • document:json格式字符串
  • root:传出参数,存储了json字符串中解析出的数据
  • collectComments:是否保存json字符串中的注释信息

bool Json::Reader::parse(const char* beginDoc, const char* endDoc, Value& root, bool colletcComments = true);

通过begindocenddoc指针定位一个json字符串,这个字符串可以是完成的json字符串,也可以是部分json字符串。


bool parse(istream& is, Value& root, bool collectComments = true);
write的文件流(ostream),read的文件流(istream)。假设要解析的json数据在磁盘文件中,is流对象指向一个磁盘文件,读操作

举例

序列化

假设我们要将下面的数据进行序列化:

复制代码
name = Jack;
age = 12;
message = hello world;
time = 2025.1.10

代码如下:

cpp 复制代码
#include <iostream>
#include <jsoncpp/json/json.h>
  
using namespace std;

int main()
{
    // 创建一个Json::Value对象
    Json::Value root;

    // 向对象中添加数据
    root["name"] = "Jack";
    root["age"] = 22;
    root["messmge"] = "hello world";
    root["time"] = "2025.1.10";

    // 创建一个Json::FastWriter
    Json::FastWriter w;
  
    // 创建一个Json::StyledWriter
    Json::StyledWriter w1;
  
    // 将Json::Value对象转换为字符串
    string output = w.write(root);
    string res = w1.write(root);
  
    // 打印输出
    cout << "FastWriter格式:" << endl;
    cout << output << endl;

    cout << "StyledWriter格式:" << endl;
    cout << res << endl;
    return 0;
}

我们可以发现顺序是按照字母顺序进行排列的。Fastwriter类和StyledWriter类的序列化形式的数据内容是一样的,区别在于序列化格式不同。

  • StyledWriter:会给json数据添加换行,让数据看起来更美观。

  • FastWriter:去掉所有的换行,数据之间用逗号隔开。整个json就是一行数据。
    如过我们的数据需要用数组和对象来进行表示,其中用[]是数组,用{}是对象,如下:

    name = Jack;
    age = 12;
    message = hello world;
    time = 2025.1.10
    [
    "School",
    "student"
    ]
    {
    "China":"Beijing",
    "US":"NewYork"
    }

那么代码就可以这样写:

cpp 复制代码
#include <iostream>
#include <jsoncpp/json/json.h>

using namespace std;
int main()
{
    // 创建一个Json::Value对象
    Json::Value root;
  
    // 向对象中添加数据
    root["name"] = "Jack";
    root["age"] = 22;
    root["message"] = "hello world";
    root["time"] = "2025-01-10"; 

    // 创建数组并添加到root中合适的键对应位置
    Json::Value arr;
    arr.append("School");
    arr.append("Student");
    root["tags"] = arr; // 修改添加方式,让数组作为"tags"键对应的值

    // 创建子对象并添加到root中合适的键对应位置
    Json::Value object;
    object["China"] = "Beijing";
    object["US"] = "NewYork";
    root["locations"] = object; // 修改添加方式,让该对象作为"locations"键对应的值
    
    // 创建一个Json::StyledWriter
    Json::StyledWriter w1;
    
    // 将Json::Value对象转为字符串
    string res = w1.write(root);
  
    // 打印输出
    cout << "StyledWriter格式:" << endl;
    cout << res << endl;

    return 0;
}

反序列化

cpp 复制代码
int main()
{

    std::string str = "{\"content\": \"Hello JsonCpp\"}";
    Json::Reader reader;
    Json::Value root;
    if (reader.parse(str, root))
        std::cout << root["content"].asString() << std::endl;
    return 0;
}

调用 Reader.parse() 接口尝试解析 json 字符串 str,当 str 满足 json 格式之后,调用 Value[] 操作符将 "content" 的值取出来,然后再进行类型转换,取出实际的类型数据。


cpp 复制代码
int main()
{
    Json::Value part1;
    part1["haha"] = "haha";
    part1["hehe"] = "hehe";
    
    Json::Value root;
    root["x"] = 100;
    root["y"] = 200;
    root["op"] = '+';
    root["desc"] = "this is a + oper";
    root["test"] = part1;
    //Json::FastWriter w;
    Json::StyledWriter w;
    std::string res = w.write(root);
    std::cout << res << std::endl;
    
    sleep(3);
    
    Json::Value v;
    Json::Reader r;
    r.parse(res, v);
    int x = v["x"].asInt();  // 类型转换
    int y = v["y"].asInt();
    char op = v["op"].asInt();
    std::string desc = v["desc"].asString();
    Json::Value temp = v["test"];
    std::cout << x << std::endl;
    std::cout << y << std::endl;
    std::cout << op << std::endl;
    std::cout << desc << std::endl;
    
    return 0;
}
相关推荐
树℡独32 分钟前
ns-3仿真之应用层(五)
服务器·网络·tcp/ip·ns3
嵩山小老虎1 小时前
Windows 10/11 安装 WSL2 并配置 VSCode 开发环境(C 语言 / Linux API 适用)
linux·windows·vscode
Fleshy数模2 小时前
CentOS7 安装配置 MySQL5.7 完整教程(本地虚拟机学习版)
linux·mysql·centos
a41324472 小时前
ubuntu 25 安装vllm
linux·服务器·ubuntu·vllm
Configure-Handler2 小时前
buildroot System configuration
java·服务器·数据库
津津有味道2 小时前
易语言TCP服务端接收刷卡数据并向客户端读卡器发送指令
服务器·网络协议·tcp·易语言
Fᴏʀ ʏ꯭ᴏ꯭ᴜ꯭.3 小时前
Keepalived VIP迁移邮件告警配置指南
运维·服务器·笔记
Genie cloud3 小时前
1Panel SSL证书申请完整教程
服务器·网络协议·云计算·ssl
一只自律的鸡3 小时前
【Linux驱动】bug处理 ens33找不到IP
linux·运维·bug
17(无规则自律)4 小时前
【CSAPP 读书笔记】第二章:信息的表示和处理
linux·嵌入式硬件·考研·高考