首页 游戏 软件 资讯 排行榜 专题
首页
编程语言
c++如何将内存中的Protobuf对象转为Json文本【技巧】

c++如何将内存中的Protobuf对象转为Json文本【技巧】

热心网友
98
转载
2026-05-06

C++如何将内存中的Protobuf对象转为Json文本【技巧】

c++如何将内存中的Protobuf对象转为Json文本【技巧】

免费影视、动漫、音乐、游戏、小说资源长期稳定更新! 👉 点此立即查看 👈

先明确一个核心事实:Protobuf 3 默认并不支持直接序列化为JSON。很多开发者初次尝试时,会下意识地寻找一个类似 SerializeToJsonString() 的方法,结果发现根本不存在。这其实是一个常见的认知误区。

Protobuf 3 默认不支持直接序列化为 JSON

Google 官方的 Protobuf C++ 库(从 v3.0 开始)在设计上就将核心序列化与 JSON 转换功能分开了。基础库只负责二进制格式的读写,而将对象转为 JSON 文本这个“高级”功能,被放在了独立的工具模块里。

具体来说,你在网上教程里看到的 JsonPrintOptionsMessageToJsonString() 函数,它们的真实地址是 google/protobuf/util/json_util.h。这个头文件隶属于 protobuf::util 工具模块,需要你显式地链接对应的库文件(通常是 libprotobuf-util)。

如果你忽略了这一步,编译时就会遇到那个经典的链接错误:undefined reference to 'google::protobuf::util::MessageToJsonString'。这本质上就是告诉开发者:你只带了核心引擎,但忘了装变速箱。

所以,正确的准备工作是:

  • 包含正确的头文件#include
  • 链接必要的库:在编译命令中添加 -lprotobuf -lprotobuf-util(如果使用 CMake,则是在 target_link_libraries 中添加 protobufprotobuf_util)。
  • 检查版本:确保你的 Protobuf 版本不低于 3.0.0,早期的版本没有这个工具模块。

使用 MessageToJsonString() 的基本流程

搞定了依赖,实际转换就非常简单了。MessageToJsonString() 是最直接、最轻量的方式,非常适合用于调试日志或简单的数据导出。

它的默认行为可以概括为“规范 JSON”模式:字段名会自动从 proto 定义的小驼峰(如 userName)转换为下划线形式(user_name),所有等于默认值的字段(比如 int32 类型的 0)会被省略,未知字段也不会被输出。

#include 
#include "your_message.pb.h"

YourMessage msg;
msg.set_id(123);
msg.set_name("test");

std::string json;
google::protobuf::util::MessageToJsonString(msg, &json);
// 此时 json 的内容将是:{"id":123,"name":"test"}

几个需要注意的细节:

  • 默认输出的 JSON 字符串是紧凑格式,没有换行和缩进。如果需要更易读的“美化”格式,需要通过选项开启。
  • 如果字段类型是 bytes,默认会进行 Base64 编码后输出。
  • 对于嵌套的 message 或 repeated 数组字段,函数会自动递归处理,开发者无需手动遍历,这一点非常省心。

控制 JSON 输出行为:JsonPrintOptions 关键参数

默认行为对调试很友好,但在生产环境中,往往需要根据前后端约定进行定制。比如,前端要求字段名保持原样(PascalCase),或者要求即使值为 0 的字段也必须出现在 JSON 中。

这时候,JsonPrintOptions 就派上用场了。它提供了一系列开关,让你能精细控制输出结果。

立即学习“C++免费学习笔记(深入)”;

google::protobuf::util::JsonPrintOptions options;
options.preserve_proto_field_names = true;  // 使用 proto 中定义的原始字段名,不进行任何转换
options.always_print_primitive_fields = true;  // 强制输出所有原始类型字段,即使其值为默认值(如 int32 id = 0; 会输出 "id": 0)
options.add_whitespace = true;  // 输出带换行和缩进(2个空格)的美化格式
options.format_int64_as_number = true;  // 将 int64/uint64 字段输出为 JSON 数字,而不是默认的字符串(为防止 Ja vaScript 中的大数精度丢失)

std::string json;
google::protobuf::util::MessageToJsonString(msg, &json, options);

这里有几个实用的经验:

  • preserve_proto_field_names 可能是对接老系统时最常用的选项,它能确保字段名在传输过程中“原汁原味”。
  • format_int64_as_number 需要谨慎使用。虽然输出为数字更符合直觉,但 Ja vaScript 的安全整数上限是 2^53-1。如果 int64 的值可能超过这个范围,保持字符串形式反而是更安全的选择,以避免前端精度丢失。
  • 如果不传递 options 参数,函数内部会使用一个所有值均为默认的 JsonPrintOptions 对象,也就是我们最开始提到的“规范 JSON”模式。

遇到 unknown field 或 Any 类型怎么办

Protobuf 的 Any 类型和未知字段(比如用旧版 proto 生成的程序收到了带有新字段的数据)在序列化为 JSON 时会遇到一些特殊情况。默认情况下,它们会被直接忽略。

如果你的业务逻辑依赖这些数据(例如动态配置下发、灰度字段),就需要一些额外处理:

  • 对于 Any 类型:通常需要先调用 Any::UnpackTo(&target_msg) 将其解包到具体的消息类型中,然后再序列化该目标消息。或者,在解析端配合使用 JsonParseOptions::ignore_unknown_fields = false 进行反向操作。
  • 对于未知字段:让序列化函数直接输出未知字段是比较复杂的。即便在解析端设置了 ignore_unknown_fields = false,打印端默认仍然不会输出。一个高级做法是利用反射 API,通过 GetReflection()->GetUnknownFields() 获取未知字段并手动拼接成 JSON,但这在实践中相当少见。
  • 更务实的建议:对于长期存在的需求,最好的办法是升级 .proto 文件定义并重新生成代码,让字段变得“已知”。或者,可以约定将 Any 字段的内容封装为一个标准的 JSON 字符串,由业务层在需要时进行二次解码。

说到底,技术实现本身并不复杂。真正的挑战在于前期的设计决策:哪些字段必须出现?字段名遵循什么命名规则?int64 大整数如何安全传递?未知字段到底有没有必要暴露给 JSON?把这些想清楚,往往比写那几行转换代码要花费更多的时间。

来源:https://www.php.cn/faq/2322517.html
免责声明: 游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

相关攻略

如何从 Go 语言的空接口中安全提取 JSON 解析后的值
编程语言
如何从 Go 语言的空接口中安全提取 JSON 解析后的值

如何在 Go 语言中安全地从空接口提取嵌套 JSON 字段 在 Go 语言开发中,处理 JSON 数据时经常遇到一个典型场景:使用 json Unmarshal 函数解析未知或动态结构的 JSON 数据,结果被存储在一个 interface{} 空接口类型中。面对这个“黑盒”,如何安全、高效且准确地

热心网友
05.05
如何在 Go 中将 map 数据完整转换为 JSON 数组格式
编程语言
如何在 Go 中将 map 数据完整转换为 JSON 数组格式

本文详细讲解在 Go 语言中如何将 map 数据完整转换为 JSON 数组格式,解决遍历赋值错误导致数据丢失以及 int 类型键无法直接序列化的问题,并提供可直接运行的优化代码示例。 Go 语言 map 转 JSON 数组完整教程与常见问题解决 在 Go 语言 Web 开发实践中,将内存中的 map

热心网友
05.05
如何从 Go 语言的空接口中安全提取 JSON 解析后的字段值
编程语言
如何从 Go 语言的空接口中安全提取 JSON 解析后的字段值

如何从 Go 语言的空接口中安全提取 JSON 解析后的字段值 本文深入讲解在 Go 语言中,如何对通过 json Unmarshal 解析到 interface{} 类型的动态 JSON 数据进行安全的类型断言与字段访问。重点演示如何准确获取嵌套的字符串字段(例如 “name”),并提供可直接运行

热心网友
05.05
VSCode快捷生成注释块_支持JSDoc风格的注释插件
编程语言
VSCode快捷生成注释块_支持JSDoc风格的注释插件

VSCode原生 **回车仅支持简单函数签名,Document This插件可稳定生成跨语言注释 先明确一个核心事实:在VSCode里,单纯靠输入 ** 然后回车,确实能生成一个注释块,但它的能力相当有限。这个原生功能只能识别最简单的函数签名。如果你想要稳定、可定制,并且能跨多种语言生成高质量注释

热心网友
05.04
VSCode快速生成注释:使用kdoc或JSDoc插件生成标准文档
编程语言
VSCode快速生成注释:使用kdoc或JSDoc插件生成标准文档

VSCode快速生成注释:使用KDoc或JSDoc插件生成标准文档 先明确一个核心概念:KDoc是Kotlin的专用注释格式,VSCode默认并不支持它的自动生成。 你真正想用的,大概率是服务于Ja vaScript或TypeScript的JSDoc,可别把两者搞混了。 为什么敲 ** 回车没反应

热心网友
05.03

最新APP

宝宝过生日
宝宝过生日
应用辅助 04-07
台球世界
台球世界
体育竞技 04-07
解绳子
解绳子
休闲益智 04-07
骑兵冲突
骑兵冲突
棋牌策略 04-07
三国真龙传
三国真龙传
角色扮演 04-07

热门推荐

商业帝国大亨好玩吗 商业帝国大亨玩法简介
游戏攻略
商业帝国大亨好玩吗 商业帝国大亨玩法简介

商业帝国大亨:一款点击就能征服宇宙的财富游戏? 近期,手游圈的目光似乎被一款名为《商业帝国大亨》的新作吸引了。不少玩家都在询问:这款游戏到底好不好玩?值不值得投入时间?今天,我们就来深入剖析一下它的玩法核心与特色,看看它能否满足你对“商业帝国”的想象。 1 核心玩法评析:从点击屏幕到宇宙财团 如果

热心网友
05.06
异环一咖舍店铺装修方案推荐 店铺经营怎么装修
游戏攻略
异环一咖舍店铺装修方案推荐 店铺经营怎么装修

异环一咖舍店铺装修方案分享:店铺经营怎么装修 在《异环》的世界里,经营自己的店铺无疑是件充满乐趣的事。看着人气攀升、收入增长,那份成就感不言而喻。不过,很多新手玩家容易踏入一个误区:一上来就冲着最华丽的摆件去,结果投入巨大,收益提升却未必理想。今天,我们就来聊聊如何用最精明的策略,搞定你的“一咖舍”

热心网友
05.06
鸣潮3.3版本声骸管理方案推荐 3.3版本声骸管理有没有方案码
游戏攻略
鸣潮3.3版本声骸管理方案推荐 3.3版本声骸管理有没有方案码

鸣潮3 3版本声骸管理方案推荐 随着鸣潮3 3版本的到来,一次全面的声骸系统更新在所难免。特别是针对那些拥有特殊机制的角色,如何高效管理你的声骸库存,成了不少指挥官当前的头等大事。好消息是,新版本支持通过方案码一键导入配置,这无疑大大提升了效率。那么,当前版本有哪些值得关注的方案,又该如何灵活运用呢

热心网友
05.06
梦幻西游175神木怎么配装备
游戏攻略
梦幻西游175神木怎么配装备

梦幻西游神木林175级装备搭配推荐 先来看头盔的选择。这是一件130级的罗汉金钟男头,套装点化成了蜃气妖,并且打上了13锻月亮石。对于神木林这样的法系门派来说,蜃气妖套能直接提升灵力,是核心选择之一。而罗汉金钟这个特技,在高端任务和PK中的重要性不言而喻,关键时刻一个罗汉,往往能扭转战局。用高锻数的

热心网友
05.06
梦幻西游175级魔王怎么搭配装备
游戏攻略
梦幻西游175级魔王怎么搭配装备

梦幻西游魔王寨175装备搭配推荐 先来看头盔的选择。一件160级附带光辉之甲特技、且激活了长眉灵猴套装效果的头盔,无疑是法系门派的上乘之选。更难得的是,它还额外附加了4 58%的法术暴击伤害属性。为了最大化生存能力,这颗头盔被打上了16锻月亮石,将防御堆砌到了一个相当可观的程度。对于追求极致输出的魔

热心网友
05.06