C++中JSON序列化和反序列化的实现

C++中JSON序列化和反序列化的实现 原因那么为什么需要序列化和反序列化呢笔者的体会是不序列化/反序列化也行只不过在进行相应的读取和写入操作的时候会非常繁琐。比如一个配置文件更新假设我们有一个博客系统的配置包含站点标题、作者名、是否启用评论、默认封面地址等多个字段。如果不使用序列化机制每次读取配置时就得手动打开 JSON或 YAML、INI文件逐行解析判断每个字段是否存在、类型是否正确再一一赋值给程序中的变量而当用户修改了某个设置、需要保存回文件时又得手动拼接字符串、处理引号转义、数组格式、缩进对齐……稍有不慎就会生成格式错误的文件导致程序下次启动失败。更麻烦的是一旦配置结构发生变化——比如新增一个 theme 字段或者把 enableComments 拆成 enablePostComments 和 enablePageComments——我们就不得不同时修改读取逻辑、写入逻辑、默认值初始化、错误处理等多处代码极易遗漏或出错。而有了序列化和反序列化这一切就变得简洁而可靠定义好结构体或类注册对应的转换函数一行代码就能从 JSON 构造出完整的配置对象另一行代码就能把修改后的对象写回文件。字段增减只需调整结构体读写逻辑几乎无需改动既减少了重复劳动也大大提升了健壮性和可维护性。因此序列化/反序列化并非“必须”但它是对抗复杂性、提升开发效率和系统可靠性的重要工具。3. 实现3.1 示例这里就举例实现一下 C 中 JSON 序列化和反序列化。如果要反序列化成 C 可以使用的内存对象最好的数据容器就是 struct 。当然 class 也可以不过我们都知道 struct 和 class 的区别struct 的成员一般默认是公有的而 class 的成员一般默认是私有的。因此struct 适合以数据成员为主的比较简单的对象class 适合以方法成员为主的比较复杂的对象。笔者使用 nlohmann/json 中序列化/反序列化一个表达博客的数据对象BlogMeta.h代码语言javascriptAI代码解释#include nlohmann/json.hpp #include string #include vector #include PostStats.h namespace DataTransfer { /// brief 一篇博客的元数据 struct BlogMeta { std::string id; std::string title; std::string summary; std::string createdTime; std::string updatedTime; std::string coverAddress; int64_t isDraft; std::vectorstd::string tagNames; std::vectorstd::string categoryNames; PostStats postStats; // 提供序列化接口 friend void to_json(nlohmann::json j, const BlogMeta s); // 提供反序列化接口 friend void from_json(const nlohmann::json j, BlogMeta s); };BlogMeta.cpp代码语言javascriptAI代码解释#include BlogMeta.h #include Util/GetTime.h namespace DataTransfer { // 提供序列化接口 void to_json(nlohmann::json j, const BlogMeta s) { j nlohmann::json{{id, s.id}, {title, s.title}, {summary, s.summary}, {createdTime, s.createdTime}, {updatedTime, s.updatedTime}, {coverAddress, s.coverAddress}, {isDraft, s.isDraft}, {tagNames, s.tagNames}, {categoryNames, s.categoryNames}, {postStats, s.postStats}}; } // 提供反序列化接口 void from_json(const nlohmann::json j, BlogMeta s) { s.id j.at(id).getstd::string(); s.title j.at(title).getstd::string(); s.summary j.at(summary).getstd::string(); s.createdTime j.at(createdTime).getstd::string(); s.updatedTime j.value(updatedTime, Util::GetCurrentTime()); //提供默认值增加稳定性 s.coverAddress j.at(coverAddress).getstd::string(); s.isDraft j.at(isDraft).getint64_t(); s.tagNames j.at(tagNames).getstd::vectorstd::string(); s.categoryNames j.at(categoryNames).getstd::vectorstd::string(); s.postStats j.at(postStats).getPostStats(); } } // namespace DataTransfer其中to_json/from_json就是对应的 序列化 / 反序列化接口。只需要填充这一对接口就可以通过赋值运算符实现自动序列化和反序列化代码语言javascriptAI代码解释#include iostream #include nlohmann/json.hpp #include BlogMeta.h // 包含你的 BlogMeta 定义 int main() { using namespace DataTransfer; using json nlohmann::json; // ---------------------------- // 1. 创建一个 BlogMeta 对象内存中的数据 // ---------------------------- BlogMeta blog; blog.id blog-001; blog.title 深入理解 C 序列化; blog.summary 本文介绍如何使用 nlohmann/json 进行安全高效的序列化。; blog.createdTime 2025-12-22T10:00:00Z; blog.updatedTime 2025-12-22T17:00:00Z; // 可省略反序列化时会用默认值 blog.coverAddress /images/cpp_serialization.jpg; blog.isDraft 0; blog.tagNames {C, JSON, Serialization}; blog.categoryNames {Programming, Tutorial}; blog.postStats {1200, 85, 23}; // ---------------------------- // 2. 序列化BlogMeta → JSON // ---------------------------- json j blog; // 自动调用 to_json std::cout 【序列化结果】\n j.dump(2) \n\n; // ---------------------------- // 3. 反序列化JSON → BlogMeta // ---------------------------- // 模拟从文件或网络接收到的 JSON故意省略 updatedTime std::string jsonStr R({ id: blog-002, title: 反序列化的健壮性测试, summary: 测试缺失 updatedTime 字段时的行为, createdTime: 2025-12-20T08:30:00Z, coverAddress: /images/robust.png, isDraft: 1, tagNames: [C, Robustness], categoryNames: [Engineering], postStats: { viewCount: 450, likeCount: 30, commentCount: 5 } }); BlogMeta restoredBlog json::parse(jsonStr); // 自动调用 from_json // ---------------------------- // 4. 验证反序列化结果 // ---------------------------- std::cout 【反序列化结果】\n; std::cout ID: restoredBlog.id \n; std::cout Title: restoredBlog.title \n; std::cout UpdatedTime (应为当前默认值): restoredBlog.updatedTime \n; std::cout Is Draft: restoredBlog.isDraft \n; std::cout Tags: ; for (const auto tag : restoredBlog.tagNames) { std::cout tag ; } std::cout \n; return 0; }3.2 能力序列化 / 反序列化的能力取决于所使用的库实现的程度。例如这个例子中std 容器std::vectorstd::string也可以直接使用赋值运算符来序列化/反序列化吗确实是可以的nlohmann/json 支持所有基础类型以及绝大多数 std 容器类型的直接 序列化/ 反序列化。不过默认情况下C 数据类型与JSON的对应关系是std::string 对应字符串 int64_t uint64_t 或 double 对应数字 std::map 对应对象 std::vector 对应数组且 bool 对应布尔值。另外nlohmann/json 还支持嵌套序列化。比如这里的 PostStats 其实也是个自定义的 struct 也实现了to_json/from_json接口因此可以直接通过赋值运算符相互转换。PostStats.h:代码语言javascriptAI代码解释#pragma once #include nlohmann/json.hpp namespace DataTransfer { /// brief 一篇博客的统计信息 struct PostStats { int64_t viewCount 0; // 阅读量 int64_t likeCount 0; // 点赞量 int64_t commentCount 0; // 评论量 // 提供序列化接口 friend void to_json(nlohmann::json j, const PostStats s); // 提供反序列化接口 friend void from_json(const nlohmann::json j, PostStats s); }; } // namespace DataTransferPostStats.cpp:代码语言javascriptAI代码解释#include PostStats.h namespace DataTransfer { // 提供序列化接口 void to_json(nlohmann::json j, const PostStats s) { j nlohmann::json{{viewCount, s.viewCount}, {likeCount, s.likeCount}, {commentCount, s.commentCount}}; } // 提供反序列化接口 void from_json(const nlohmann::json j, PostStats s) { s.viewCount j.at(viewCount).getint64_t(); s.likeCount j.at(likeCount).getint64_t(); s.commentCount j.at(commentCount).getint64_t(); } } // namespace DataTransfer3.3 兼容如果 JSON 配置文件进行了升级比如增加了一个新的配置字段那么新的程序读取旧的配置文件就有可能出错所以在反序列化的时候最好使用 value() 提供默认值代码语言javascriptAI代码解释void from_json(const nlohmann::json j, BlogMeta s) { s.id j.value(id, std::string{}); s.title j.value(title, std::string{}); s.summary j.value(summary, std::string{}); s.createdTime j.value(createdTime, std::string{}); s.updatedTime j.value(updatedTime, std::string{}); // 若缺失则为空字符串 s.coverAddress j.value(coverAddress, std::string{}); s.isDraft j.value(isDraft, int64_t{0}); s.tagNames j.value(tagNames, std::vectorstd::string{}); s.categoryNames j.value(categoryNames, std::vectorstd::string{}); // 对于嵌套结构如 PostStats如果它也支持 from_json可这样处理 if (j.contains(postStats) j[postStats].is_object()) { s.postStats j[postStats].getPostStats(); } else { s.postStats PostStats{}; // 默认构造 } }另外如果业务逻辑允许能分清楚哪些是必须字段哪些是可选字段那么可以使用std::optional:代码语言javascriptAI代码解释/// brief 一篇博客的元数据健壮版支持可选字段 struct BlogMeta { std::string id; // 必填 std::string title; // 必填 std::optionalstd::string summary; std::string createdTime; // 通常必填也可改为 optional std::optionalstd::string updatedTime; std::optionalstd::string coverAddress; std::optionalint64_t isDraft; // 可选默认 false 或 0 std::optionalstd::vectorstd::string tagNames; std::optionalstd::vectorstd::string categoryNames; std::optionalPostStats postStats; // 序列化/反序列化友元声明 friend void to_json(nlohmann::json j, const BlogMeta s); friend void from_json(const nlohmann::json j, BlogMeta s); }; void to_json(nlohmann::json j, const BlogMeta s) { j nlohmann::json{ {id, s.id}, {title, s.title}, {summary, s.summary}, {createdTime, s.createdTime}, {updatedTime, s.updatedTime}, {coverAddress, s.coverAddress}, {isDraft, s.isDraft}, {tagNames, s.tagNames}, {categoryNames, s.categoryNames}, {postStats, s.postStats} }; } void from_json(const nlohmann::json j, BlogMeta s) { // 必填字段使用 at()缺失时报错 s.id j.at(id).getstd::string(); s.title j.at(title).getstd::string(); s.createdTime j.at(createdTime).getstd::string(); // 若你也想让它可选改用 value 或 optional // 可选字段直接赋值nlohmann 自动处理存在性 if (j.contains(summary)) s.summary j[summary].getstd::string(); if (j.contains(updatedTime)) s.updatedTime j[updatedTime].getstd::string(); if (j.contains(coverAddress)) s.coverAddress j[coverAddress].getstd::string(); if (j.contains(isDraft)) s.isDraft j[isDraft].getint64_t(); if (j.contains(tagNames)) s.tagNames j[tagNames].getstd::vectorstd::string(); if (j.contains(categoryNames)) s.categoryNames j[categoryNames].getstd::vectorstd::string(); if (j.contains(postStats)) s.postStats j[postStats].getPostStats(); }