目录
- 一、什么是序列化
- 二、Cereal序列化库
- 三、下载与编译
- 四、使用
一、什么是序列化
序列化在编程中有以下几个重要的原因:
-
数据存储:将数据对象序列化为一种持久化的格式,可以将其存储在文件、数据库或其他存储介质中。这样可以在程序的不同运行之间保存数据的状态,以便后续使用。
-
网络传输:当需要在网络上发送数据时,将数据序列化为一种适合传输的格式(如二进制或文本)可以确保数据能够正确地在网络中传输,并在接收端进行反序列化还原为原始的数据对象。
-
数据交换:在不同的系统或应用程序之间进行数据交换时,序列化可以提供一种通用的格式,使得数据能够在不同的环境中被理解和处理。
-
对象状态保存:序列化可以用于保存对象的当前状态,以便在需要时恢复到该状态。这在一些需要记录和恢复对象状态的场景中非常有用,例如游戏中的保存和加载功能。
-
分布式系统:在分布式系统中,序列化可以用于在不同的节点之间传递数据对象,确保数据的一致性和可靠性。
-
缓存和持久化:将数据序列化后,可以将其存储在缓存中以提高访问速度,或者将其持久化到磁盘以长期保存。
-
跨语言支持:一些序列化格式是跨语言的,这意味着可以在不同的编程语言中进行序列化和反序列化操作,促进了不同语言之间的互操作性。
通过序列化,我们可以将复杂的数据结构转换为一种可存储、可传输和可恢复的形式,方便数据的管理和处理。不同的序列化库和技术在性能、灵活性和兼容性方面可能有所不同,选择适合具体需求的序列化方法可以提高开发效率和系统的可维护性。
一般使用的序列化有json、protobuf,但是这些方式局限性很大,只支持基本数据类型和数组,对于智能指针、map这些复杂数据类型不支持,使用起来极为不便。
二、Cereal序列化库
Cereal 是一个 C++11 的序列化库,它是现代、轻量级、快速和灵活的,可以用来保存和加载几乎所有类型的数据。
Cereal 支持将自定义数据类型序列化成 JSON、XML、二进制,反之也可以实现反序列化。
三、下载与编译
Cereal GitHub 地址:https://github.com/USCiLab/cereal。
Cereal 是一个 head only 的库,无需编译。
四、使用
Cereal可以大体分为三类,结构体序列化反序列化、数据与JSON序列化反序列化、数据与XML序列化反序列化。
结构体序列化反序列化
#include <iomanip>
#include <iostream>
#include <sstream>
#include <vector>
#include <unordered_map>
#include <queue>
#include <fstream>#include "cereal/archives/json.hpp"
#include "cereal/archives/binary.hpp"// 序列化哪种类型就包含哪种类型的hpp
#include "cereal/types/string.hpp"
#include "cereal/types/vector.hpp"
#include "cereal/types/unordered_map.hpp"
#include "cereal/types/queue.hpp"
#include "cereal/types/memory.hpp"// 自定义结构体
struct A
{int n;template<class Archive>void serialize(Archive& archive) // 序列化函数,函数名必须为serialize{archive(n);}
};struct demo_struct
{ int id{};std::string name;std::vector<double> values;std::unordered_map<int, int> mp;std::queue<int> qu;std::shared_ptr<int> num;A a;template<class Archive>void serialize(Archive& archive) // 序列化函数{archive(id, name, values,mp,qu,num,a);}
};// 序列化结构体
void SerializeStruc()
{demo_struct data; // 自定义对象data.id = 1;data.name = "Example";data.values = { 3.14, 2.718, 1.414 };data.num = std::make_shared<int>(2);data.mp[1] = 1;data.mp[2] = 2;data.qu.push(1);data.qu.push(2);data.a.n = 3;// 将结构体序列化为Binary字符串std::ostringstream oss(std::ios::binary);{cereal::BinaryOutputArchive archive(oss);archive(data);}// 输出序列化后的Binary字符串到控制台std::string serializedData = oss.str();for (char byte : serializedData) {std::cout << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(static_cast<unsigned char>(byte)) << " ";}std::cout << std::endl;// 从Binary字符串反序列化结构体{demo_struct loaded_data;std::istringstream iss(oss.str());cereal::BinaryInputArchive archive(iss);archive(loaded_data);// 显示反序列化后的数据std::cout << "\nLoaded ID: " << loaded_data.id << std::endl;std::cout << "Loaded Name: " << loaded_data.name << std::endl;std::cout << "Loaded Num: " << *loaded_data.num << std::endl;std::cout << "Loaded Values: ";for (const auto& value : loaded_data.values) {std::cout << value << " ";}std::cout << std::endl;std::cout << "Loaded mp: ";for (const auto& it : loaded_data.mp)std::cout << it.first << "=" << it.second << " ";std::cout << std::endl;std::cout << "Loaded qu: ";while (!loaded_data.qu.empty()){std::cout << loaded_data.qu.front() << " ";loaded_data.qu.pop();}std::cout << std::endl;std::cout << "Loaded a: " << loaded_data.a.n << std::endl;}
}int main() {SerializeStruc();system("pause");return 0;
}
数据与JSON序列化反序列化
1、将基本数据类型保存为JSON文件
// 写入JSON文件std::ofstream file("out.json");cereal::JSONOutputArchive archive(file);std::string s[] = { "this is a string", " 中文string也是可以支持的" };std::vector<double> vec = { 1.00001, 2e3, 30.1, -4, 5 };// 相当于将vec和s作为JSON的KEY值,见如下JSON文件archive(CEREAL_NVP(vec), CEREAL_NVP(s));// 文件关闭后写入的json文件少一个大括号,不知道为什么// file.close();
2、读取JSON文件数据
std::ifstream readFile("out.json");cereal::JSONInputArchive readArchive(readFile);// 注意变量名和写入时需要一致std::vector<double> vec;std::string s[2];// 相当于按照KEY值去读readArchive(CEREAL_NVP(vec),CEREAL_NVP(s));for (const auto& it : vec)std::cout << it << " ";std::cout << s[0] << " " << s[1] << std::endl;readFile.close();
3、自定义结构体与JSON的序列化反序列化
#include <iomanip>
#include <iostream>
#include <sstream>
#include <vector>
#include <unordered_map>
#include <queue>
#include <fstream>#include "cereal/archives/json.hpp"
#include "cereal/archives/binary.hpp"// 序列化哪种类型就包含哪种类型的hpp
#include "cereal/types/string.hpp"
#include "cereal/types/vector.hpp"
#include "cereal/types/unordered_map.hpp"
#include "cereal/types/queue.hpp"
#include "cereal/types/memory.hpp"// 自定义结构体
struct A
{int n;template<class Archive>void serialize(Archive& archive) // 序列化函数,函数名必须为serialize{archive(n);// 不将类型名作为KEY值,采用默认,注意区别//archive(cereal::make_nvp("n", n));}
};struct demo_struct
{ int id{};std::string name;std::vector<double> values;std::unordered_map<int, int> mp;std::queue<int> qu;std::shared_ptr<int> num;A a;template<class Archive>void serialize(Archive& archive) // 序列化函数{archive(id, name, values,mp,qu,num,a);// 不将类型名作为KEY值,采用默认,注意区别//archive(cereal::make_nvp("id", id), cereal::make_nvp("name", name)// , cereal::make_nvp("values", values), cereal::make_nvp("mp", mp)// , cereal::make_nvp("qu", qu), cereal::make_nvp("num", num)// , cereal::make_nvp("A", a));}
};// 序列化json
void SerializeJson()
{demo_struct data; // 自定义对象data.id = 1;data.name = "Example";data.values = { 3.14, 2.718, 1.414 };data.num = std::make_shared<int>(2);data.mp[1] = 1;data.mp[2] = 2;data.qu.push(1);data.qu.push(2);data.a.n = 3;// 将结构体序列化为JSON字符串std::ostringstream oss;{cereal::JSONOutputArchive archive(oss);archive(cereal::make_nvp("demo_struct", data));}// 输出序列化后的JSON字符串到控制台std::cout << "序列化为 JSON:\n" << oss.str() << std::endl;// 从JSON字符串反序列化结构体{demo_struct loaded_data;std::istringstream iss(oss.str());cereal::JSONInputArchive archive(iss);archive(cereal::make_nvp("demo_struct", loaded_data));// 显示反序列化后的数据std::cout << "\nLoaded ID: " << loaded_data.id << std::endl;std::cout << "Loaded Name: " << loaded_data.name << std::endl;std::cout << "Loaded Num: " << *loaded_data.num << std::endl;std::cout << "Loaded Values: ";for (const auto& value : loaded_data.values)std::cout << value << " ";std::cout << std::endl;std::cout << "Loaded mp: ";for (const auto& it : loaded_data.mp)std::cout << it.first << "=" << it.second << " ";std::cout << std::endl;std::cout << "Loaded qu: ";while (!loaded_data.qu.empty()){std::cout << loaded_data.qu.front() << " ";loaded_data.qu.pop();}std::cout << std::endl;std::cout << "Loaded a: " << loaded_data.a.n << std::endl;}
}int main() {SerializeJson();system("pause");return 0;
}
4、XML序列化反序列化
XML用法和JSON用法一致,区别在于
// 序列化
cereal::XMLOutputArchive // XML序列化
cereal::JSONOutputArchive // JSON序列化// 反序列化
cereal::XMLInputArchive
cereal::JSONInputArchive
#include <iomanip>
#include <iostream>
#include <sstream>
#include <vector>
#include <unordered_map>
#include <queue>
#include <fstream>#include "cereal/archives/json.hpp"
#include "cereal/archives/xml.hpp"
#include "cereal/archives/binary.hpp"// 序列化哪种类型就包含哪种类型的hpp
#include "cereal/types/string.hpp"
#include "cereal/types/vector.hpp"
#include "cereal/types/unordered_map.hpp"
#include "cereal/types/queue.hpp"
#include "cereal/types/memory.hpp"// 自定义结构体
struct A
{int n;template<class Archive>void serialize(Archive& archive) // 序列化函数,函数名必须为serialize{//archive(n);archive(cereal::make_nvp("n", n));}
};struct demo_struct
{ int id{};std::string name;std::vector<double> values;std::unordered_map<int, int> mp;std::queue<int> qu;std::shared_ptr<int> num;A a;template<class Archive>void serialize(Archive& archive) // 序列化函数{//archive(id, name, values,mp,qu,num,a);archive(cereal::make_nvp("id", id), cereal::make_nvp("name", name), cereal::make_nvp("values", values), cereal::make_nvp("mp", mp), cereal::make_nvp("qu", qu), cereal::make_nvp("num", num), cereal::make_nvp("A", a));}
};void SerializeXML()
{demo_struct data; // 自定义对象data.id = 1;data.name = "Example";data.values = { 3.14, 2.718, 1.414 };data.num = std::make_shared<int>(2);data.mp[1] = 1;data.mp[2] = 2;data.qu.push(1);data.qu.push(2);data.a.n = 3;// 将结构体序列化为XML字符串std::ostringstream oss;{cereal::XMLOutputArchive archive(oss);archive(cereal::make_nvp("demo_struct", data));}// 输出序列化后的XML字符串到控制台std::cout << "序列化为 XML:\n" << oss.str() << std::endl;// 从XML字符串反序列化结构体{demo_struct loaded_data;std::istringstream iss(oss.str());cereal::XMLInputArchive archive(iss);archive(cereal::make_nvp("demo_struct", loaded_data));// 显示反序列化后的数据std::cout << "\nLoaded ID: " << loaded_data.id << std::endl;std::cout << "Loaded Name: " << loaded_data.name << std::endl;std::cout << "Loaded Num: " << *loaded_data.num << std::endl;std::cout << "Loaded Values: ";for (const auto& value : loaded_data.values)std::cout << value << " ";std::cout << std::endl;std::cout << "Loaded mp: ";for (const auto& it : loaded_data.mp)std::cout << it.first << "=" << it.second << " ";std::cout << std::endl;std::cout << "Loaded qu: ";while (!loaded_data.qu.empty()){std::cout << loaded_data.qu.front() << " ";loaded_data.qu.pop();}std::cout << std::endl;std::cout << "Loaded a: " << loaded_data.a.n << std::endl;}
}