在C++项目开发中,我们经常会遇到需要把内存中的复杂嵌套结构体数据持久化存储的需求,转换为JSON格式是最通用的选择之一,既可以跨平台读取,也方便后续扩展。下面介绍一套完整的实现方案。

准备工作
我们选择nlohmann_json库来实现JSON转换,这是目前C++生态中最流行、易用性最高的JSON处理库,支持自动序列化自定义结构体,无需手动拼接字符串。首先需要通过包管理工具或者源码引入该库,在编译时添加对应的头文件路径即可。
定义嵌套结构体
先定义一个包含多层嵌套的示例结构体,包含基础类型、数组、子结构体等常见场景:
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
// 使用nlohmann_json的json别名
using json = nlohmann::json;
// 最内层的子结构体
struct Address {
std::string province;
std::string city;
std::string street;
};
// 中层的结构体
struct Contact {
std::string phone;
std::string email;
Address addr;
};
// 最外层的复杂嵌套结构体
struct User {
int id;
std::string name;
int age;
std::vector<std::string> hobbies;
Contact contact;
std::vector<Contact> emergency_contacts;
};
配置结构体到JSON的转换规则
nlohmann_json库支持通过ADL(参数依赖查找)自动识别结构体的序列化规则,我们只需要在结构体定义之后,添加对应的转换函数即可,无需修改结构体本身的代码:
// 为Address结构体配置转换规则
void to_json(json& j, const Address& addr) {
j = json{
{"province", addr.province},
{"city", addr.city},
{"street", addr.street}
};
}
void from_json(const json& j, Address& addr) {
j.at("province").get_to(addr.province);
j.at("city").get_to(addr.city);
j.at("street").get_to(addr.street);
}
// 为Contact结构体配置转换规则
void to_json(json& j, const Contact& c) {
j = json{
{"phone", c.phone},
{"email", c.email},
{"addr", c.addr}
};
}
void from_json(const json& j, Contact& c) {
j.at("phone").get_to(c.phone);
j.at("email").get_to(c.email);
j.at("addr").get_to(c.addr);
}
// 为User结构体配置转换规则
void to_json(json& j, const User& u) {
j = json{
{"id", u.id},
{"name", u.name},
{"age", u.age},
{"hobbies", u.hobbies},
{"contact", u.contact},
{"emergency_contacts", u.emergency_contacts}
};
}
void from_json(const json& j, User& u) {
j.at("id").get_to(u.id);
j.at("name").get_to(u.name);
j.at("age").get_to(u.age);
j.at("hobbies").get_to(u.hobbies);
j.at("contact").get_to(u.contact);
j.at("emergency_contacts").get_to(u.emergency_contacts);
}
结构体转JSON并写入文件
完成转换规则配置后,就可以快速实现结构体到JSON的转换,以及文件存盘操作:
#include <fstream>
#include <iostream>
int main() {
// 构造测试用的嵌套结构体数据
User user;
user.id = 1001;
user.name = "张三";
user.age = 28;
user.hobbies = {"篮球", "阅读", "编程"};
user.contact.phone = "13800138000";
user.contact.email = "zhangsan@ipipp.com";
user.contact.addr.province = "广东";
user.contact.addr.city = "深圳";
user.contact.addr.street = "科技园路1号";
Contact emergency1;
emergency1.phone = "13900139000";
emergency1.email = "lisi@ipipp.com";
emergency1.addr.province = "广东";
emergency1.addr.city = "广州";
emergency1.addr.street = "天河路2号";
user.emergency_contacts.push_back(emergency1);
// 结构体转换为JSON对象
json user_json = user;
// 写入文件,使用缩进格式方便查看
std::ofstream out_file("user_data.json");
if (out_file.is_open()) {
out_file << user_json.dump(4); // 4表示缩进空格数
out_file.close();
std::cout << "结构体已成功转换为JSON并写入文件" << std::endl;
} else {
std::cout << "打开文件失败" << std::endl;
}
return 0;
}
注意事项
- 如果结构体中有指针成员,需要额外处理指针指向的内容,避免序列化的数据缺失或者出现野指针问题。
- 对于枚举类型成员,需要额外配置枚举到整数或者字符串的转换规则,否则库无法自动识别。
- dump方法的参数可以调整JSON的输出格式,传入-1会输出无缩进的紧凑格式,适合网络传输场景。
- 如果结构体嵌套层级非常深,需要保证每一层的子结构体都配置了对应的转换规则,否则编译时会报错。
反向读取验证
如果需要从JSON文件恢复结构体,只需要读取文件内容,再转换为对应的结构体即可:
int main() {
// 从文件读取JSON内容
std::ifstream in_file("user_data.json");
if (!in_file.is_open()) {
std::cout << "打开文件失败" << std::endl;
return -1;
}
json loaded_json;
in_file >> loaded_json;
in_file.close();
// JSON转换为结构体
User loaded_user = loaded_json.get<User>();
// 验证数据正确性
std::cout << "读取到的用户姓名:" << loaded_user.name << std::endl;
std::cout << "读取到的用户城市:" << loaded_user.contact.addr.city << std::endl;
return 0;
}