云计算百科
云计算领域专业知识百科平台

序列化与反序列化技术:开发者必知的概念与应用

个人主页:chian-ocean

文章专栏-Linux

序列化与反序列化技术:开发者必知的概念与应用

    • 个人主页:chian-ocean
    • 文章专栏-Linux
  • 前言
  • (反)序列化存在的必要性
    • **什么是序列化和反序列化?(是什么)**
    • **为什么要进行序列化和反序列化(为什么)**
    • **序列化和反序列化的应用场景(干什么)**
    • 序列化和反序列化的底层是什么(原理)?
  • 手搓序列化和反序列化
    • 序列化
      • 请求序列化
        • 详细解释
        • 序列化示例
      • 响应序列化
        • **详细解释**
        • **序列化后的示例**
    • 反序列化
      • 请求反序列化
        • **详细解释**
        • 反序列化示例
      • 响应反列化
        • 详细解释
        • 反序列化示例

前言

序列化 和 反序列化 是计算机网络编程中的常见操作,尤其在客户端和服务器之间传输数据时。序列化将复杂的数据结构转化为字节流或可传输格式,而反序列化则是将传输的字节流或数据恢复成原始数据结构。通过这些操作,数据可以在不同的计算机、进程或系统之间进行高效交换。

image-20250617203733616

(反)序列化存在的必要性

什么是序列化和反序列化?(是什么)

  • 序列化:把内存中的数据(比如对象、数组、结构体等)转换为可以传输或存储的格式。通常是将数据转换为一串字节流(也可以是字符串,二进制等格式)。
  • 反序列化:将字节流或字符串转换回原始的数据结构,方便程序继续使用。

为什么要进行序列化和反序列化(为什么)

  • 将复杂数据结构转化为可传输的格式,以便能够通过网络或存储介质传输。
  • 确保跨平台和跨语言的兼容性,使得不同系统之间能够无缝交换数据。
  • 提高传输效率,尤其是在带宽受限的网络环境中,序列化格式可以通过压缩和优化数据大小来提升性能。
  • 支持数据持久化,将数据保存到文件或数据库中,方便之后的恢复和使用。
  • 支持标准化协议,确保各个系统之间按照统一协议交换数据。
  • 确保数据一致性,避免因格式不一致导致的错误。

说白了就是为了网络传输,和数据交换兼容性问题。

序列化和反序列化的应用场景(干什么)

  • 网络传输:在客户端和服务器之间交换数据时,数据需要转换成一种可以通过网络传输的格式,比如 JSON、XML 或二进制格式。
  • 数据存储:把程序中的数据保存到文件或数据库中(持久化),以便以后恢复使用。
  • 跨语言或跨平台的数据交换:不同操作系统和编程语言的数据表示方式可能不同,序列化和反序列化可以确保不同系统之间的数据传输是兼容的。

序列化和反序列化的底层是什么(原理)?

从内存流转向字节流的一个过程。如下,传入一个结构体,在不同的编译器下会有不同的结构体对齐情况。因此:

  • 在传输的时候转向字符串的过程可以称之为**序列化(Serialize)**的过程
  • 当我另外的进程(主机等等)通过解码恢复原来的结构体的过程可以称之为**反序列化(Deserialize)**的过程

image-20250617203354252

序列化过程:

  • 选择序列化格式(例如 JSON、Protobuf,也可以手写)。
  • 将数据结构中的每个字段(如整数、字符串)转换为字节流,按字段顺序组织字节流。
  • 如果需要,进行数据压缩、加密等操作。
  • 反序列化过程:

  • 接收传输过来的字节流。
  • 根据序列化格式解析字节流中的每个字段,恢复成相应的数据类型(例如整数、字符串)。
  • 如果需要,进行解压、解密等操作。
  • 手搓序列化和反序列化

    以下下的场景基于网络收发数据的过程。(客户端过网络请求服务端通两个数进行运算发送,服务端通过计算通过网络传输给客户端)

    • 我们手到请求(Request)数据序列化并且添加数据报文。

    class Requset
    {
    public:
    Requset(int data1, char op, int data2)
    : x_(data1), op_(op), y_(data2)
    {
    }
    Requset()
    {}

    private:
    int x_;
    char op_;
    int y_;
    };

    • 服务端响应计算(Calculate)的到结果传给客户端。

    class Response
    {
    public:
    Response(int ret, int code)
    : retsult_(ret), code_(code)
    {}

    Response()
    {}

    ~Response()
    {}

    private:
    int retsult_;
    int code_;
    };

    序列化

    请求序列化

    // 序列化:x + y
    bool Serialize(std::string *out)
    {
    // 构建有效载荷
    // 将整数 x_ 转换为字符串,并赋值给 s
    std::string s = std::to_string(x_);

    // 在字符串 s 后添加空格分隔符
    s += space_sep;

    // 添加运算符(例如:+,-,*,/)到字符串 s
    s += op_;

    // 再次添加空格分隔符
    s += space_sep;

    // 将整数 y_ 转换为字符串,并添加到字符串 s 后面
    s += std::to_string(y_);

    // 将构建好的字符串 s 赋值给输出参数 out
    *out = s;

    // 返回 true,表示序列化成功
    return true;
    }

    详细解释

    序列化目标:

    • 该方法的目的是将当前类对象(包括 x_、op_ 和 y_)的数据序列化成一个字符串,使得数据可以方便地传输、存储或者进行后续处理。
    • x_ 和 y_ 是整数类型的成员变量,op_ 是运算符(如 +、- 等)。

    字符串构建:

    • std::to_string(x_):将整数 x_ 转换为字符串形式。
    • space_sep:空格字符,用作运算数和运算符之间的分隔符。此变量未在方法内部声明,假设它是一个类成员变量(例如 static const std::string space_sep = " ";),用于控制序列化中使用的分隔符。
    • op_:将运算符 op_(例如 +、- 等)追加到字符串中。
    • std::to_string(y_):将整数 y_ 转换为字符串形式。

    构建最终的序列化字符串:

    • 通过连续的字符串拼接,构建出形如 x op y 的字符串(例如,3 + 5)。
    • 在运算符与操作数之间添加空格分隔符,以确保反序列化时能正确解析每个部分。

    输出结果:

    • 最终的字符串(如 "3 + 5")被赋值给 out 指向的 std::string。
    • 由于参数 out 是一个指针,因此方法会直接修改传入的 std::string,返回最终结果。

    返回值:

    • true:表示序列化成功。
    序列化示例

    序列化: Serialize 函数将 x_ 和 y_ 转换为字符串,并在它们之间添加运算符 op_,然后通过空格分隔符拼接这些字符串,最后将结果赋值给输出参数 out。x_ = 5 y_= 3 字符串out = 5 + 3;

    响应序列化

    // 序列化 result code
    bool Serialize(std::string *out)
    {
    // 构建有效载荷

    // 将整数 retsult_ 转换为字符串,并赋值给 s
    std::string s = std::to_string(retsult_);

    // 在字符串 s 后添加空格分隔符
    s += space_sep;

    // 将整数 code_ 转换为字符串,并添加到字符串 s 后面
    s += std::to_string(code_);

    // 将构建好的字符串 s 赋值给输出参数 out
    *out = s;

    // 返回 true,表示序列化成功
    return true;
    }

    详细解释
  • 序列化目标:
    • 该方法的目的是将类的两个成员变量 retsult_ 和 code_ 序列化为一个字符串,以便进行传输、存储或其他后续处理。
    • retsult_ 和 code_ 都是整数类型的成员变量,表示一些需要序列化的数据。
  • 字符串构建:
    • std::to_string(retsult_):将整数 retsult_ 转换为字符串形式。
    • space_sep:这是一个空格字符,作为分隔符,将两个整数的字符串形式分开。假设 space_sep 是一个类成员,定义了空格字符 static const std::string space_sep = " ";。
    • std::to_string(code_):将整数 code_ 转换为字符串形式。
  • 构建最终的序列化字符串:
    • 通过字符串拼接,构建形如 "retsult_ code_" 的字符串(例如,"123 456")。
    • 在两个整数之间加入空格,确保反序列化时能够正确解析每个部分。
  • 输出结果:
    • 将构建的字符串 s 赋值给输出参数 out,这是一个指向 std::string 的指针。
    • 由于 out 是指针,传入的字符串将被修改为序列化后的结果。
  • 返回值:
    • true:表示序列化操作成功。
  • 序列化后的示例

    假设 retsult_ = 123 和 code_ = 456,调用 Serialize 方法后:

    • 传入参数 out(一个 std::string*)将会包含序列化后的字符串 "123 456"。

    反序列化

    请求反序列化

    // 反序列化 x + y
    bool Deserialize(const std::string &in)
    {
    // 解析有效载荷

    // 查找第一个空格的位置(分隔符)
    auto pos1 = in.find(space_sep);
    if (pos1 == std::string::npos) // 如果没有找到空格,返回 false,表示无效的输入
    return false;

    // 提取第一个操作数(x)部分,subtring(0, pos1) 提取从 0 到第一个空格前的字符串
    std::string part_x = in.substr(0, pos1);

    // 查找最后一个空格的位置(分隔符),用于区分运算符与第二个操作数
    auto pos2 = in.rfind(space_sep);

    // 提取运算符部分(即空格之间的部分),subtring(pos1 + 1, pos2) 提取第一个空格后,第二个空格前的字符串
    std::string oper = in.substr(pos1 + 1, pos2);

    // 提取第二个操作数(y)部分,subtring(pos2 + 1) 提取最后一个空格之后的部分
    std::string part_y = in.substr(pos2 + 1);

    // 确保第二个空格的位置与第一个空格之间的距离是 2,表示运算符长度为 1(只包含一个字符)
    if (pos2 != pos1 + 2)
    return false;

    // 设置操作符 op_,存储在 pos1 + 1 处,表示操作符的位置
    op_ = in[pos1 + 1];

    // 将 part_x 和 part_y 转换为整数,存储到 x_ 和 y_ 中
    x_ = std::stoi(part_x);
    y_ = std::stoi(part_y);

    // 返回 true,表示反序列化成功
    return true;
    }

    详细解释
  • 输入字符串 in:
    • 该方法的输入参数 in 是一个包含数学表达式的字符串,格式为 "x op y"(例如 "3 + 5")。
    • space_sep 是一个字符串,通常为 " "(空格),用作分隔符,在序列化和反序列化过程中分隔操作数和运算符。
  • 查找第一个空格的位置:
    • auto pos1 = in.find(space_sep);:查找字符串中第一个空格的位置。这个位置标志着左操作数(x_)和运算符之间的分隔点。
    • 如果没有找到空格,返回 false,表示输入格式不正确。
  • 提取左操作数 x_:
    • std::string part_x = in.substr(0, pos1);:提取从字符串开头到第一个空格之前的部分,即左操作数(x_)。
    • part_x 是一个字符串,后续会将其转为整数。
  • 查找第二个空格的位置:
    • auto pos2 = in.rfind(space_sep);:查找字符串中最后一个空格的位置。这个位置通常标志着运算符(op_)和右操作数(y_)之间的分隔点。
  • 提取运算符 op_:
    • std::string oper = in.substr(pos1 + 1, pos2);:提取位于两个空格之间的部分,即运算符。这里假设运算符是一个单字符(如 +、-、* 等)。
    • oper 变量存储运算符,但实际上此变量在后续没有直接使用,可以省略。
  • 提取右操作数 y_:
    • std::string part_y = in.substr(pos2 + 1);:提取第二个空格之后的部分,即右操作数(y_)。
  • 确保运算符部分只有一个字符:
    • if (pos2 != pos1 + 2):检查第二个空格的位置,确保两个空格之间的间距为 2(即一个字符的运算符)。如果运算符不是单个字符(比如没有运算符或有多个字符),返回 false。
  • 赋值操作符 op_ 和转换操作数:
    • op_ = in[pos1 + 1];:提取运算符,位置在第一个空格后面,存储在 op_ 变量中。
    • x_ = std::stoi(part_x); 和 y_ = std::stoi(part_y);:将提取到的字符串 part_x 和 part_y 转换为整数,存储在 x_ 和 y_ 中。
  • 反序列化示例

    Deserialize 方法的目的是将一个字符串(例如 "3 + 5")反序列化为类的三个成员变量:x_(左操作数)、op_(运算符)和 y_(右操作数)

    响应反列化

    bool Deserialize(const std::string &in)
    {
    auto pos = in.find(space_sep);
    std::string res = in.substr(0, pos);
    std::string code = in.substr(pos + 1);

    if (pos != in.rfind(space_sep))
    return false;

    retsult_ = std::stoi(res);
    code_ = std::stoi(code);
    return true;
    }

    详细解释
  • 查找分隔符:

    • auto pos = in.find(space_sep); 这行代码在输入字符串 in 中查找分隔符 space_sep 的第一个出现位置,并返回这个位置的索引值。
  • 提取字符串的第一部分:

    • std::string res = in.substr(0, pos); 这行代码提取从字符串 in 开始到分隔符位置之前的子串(不包括分隔符本身),这个子串用来存储第一个整数。
  • 提取字符串的第二部分:

    • std::string code = in.substr(pos + 1); 这行代码从分隔符的下一个位置开始,到字符串的末尾,提取出第二个子串,用来存储第二个整数。
  • 检查是否有多个分隔符:

    • if (pos != in.rfind(space_sep)) 这行代码检查在字符串中是否有多个 space_sep 分隔符。rfind 返回 space_sep 在字符串中最后一次出现的位置。如果第一个分隔符位置 pos 和最后一个分隔符位置 in.rfind(space_sep) 不相等,说明字符串中有多个分隔符,因此函数会返回 false,表示字符串格式无效。
  • 将子串转换为整数:

    • 如果字符串中只有一个分隔符,接下来的两行代码将子串转换为整数:

      • retsult_ = std::stoi(res); 将字符串 res 转换为整数,并赋值给 retsult_。

      • code_ = std::stoi(code); 将字符串 code 转换为整数,并赋值给 code_。

  • 反序列化示例

    输入字符串:我们在 main 函数中使用了一个输入字符串 "123 456",它包含两个整数,分别由空格分隔。

    反序列化:调用 obj.Deserialize(input),函数尝试从字符串中提取出这两个整数,并赋值给 retsult_ 和 code_。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 序列化与反序列化技术:开发者必知的概念与应用
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!