跳到主要内容Protobuf、JSON、XML 核心对比与 C++ 实现 | 极客日志C++
Protobuf、JSON、XML 核心对比与 C++ 实现
Protobuf、JSON、XML 是三种常用的数据交换技术。Protobuf 采用二进制编码,体积小效率高;JSON 基于文本键值对,易读且生态完善;XML 使用自定义标签,结构严谨但冗余度高。文章通过 C++ 代码示例演示了三者的序列化与反序列化流程,并对比了可读性、体积、解析效率等特性,最后给出了不同业务场景下的选型建议。
laoliangsh2 浏览 在数据交换和序列化领域,Protobuf、JSON、XML 是三种最常用的技术,它们各自有不同的设计理念和适用场景。本文将从新手视角出发,先明确三者的核心定义,再由浅入深讲解它们的语法、C++ 实现方式及核心差异,帮助你快速理解并掌握这三种技术的核心用法。
一、核心定义:先搞懂'是什么'
1. Protobuf(Protocol Buffers)
Protobuf 是 Google 开发的轻量级、跨语言、跨平台的结构化数据序列化协议,通过自定义数据结构定义文件(.proto),编译生成对应语言的代码,实现高效的数据编码和解码,核心特点是'紧凑、高效、可扩展'。
2. JSON(JavaScript Object Notation)
JSON 是一种轻量级的文本数据交换格式,基于 JavaScript 对象语法,但独立于语言,采用键值对的形式组织数据,具有易读、易写、解析成本低的特点,是当前互联网领域最主流的数据交换格式。
3. XML(Extensible Markup Language)
XML 是一种可扩展的标记语言,通过自定义标签来描述数据结构,强调数据的结构化和可读性,语法严格(闭合标签、命名空间等),常用于配置文件、传统系统数据交换场景。
二、基础语法:从'怎么写'开始
1. 数据结构示例:统一场景
为了方便对比,我们以'用户信息'为统一场景,分别用三种技术定义包含「ID、姓名、年龄、爱好列表」的用户数据结构。
(1)Protobuf:先定义后使用
Protobuf 需先编写 .proto 定义文件,再编译生成代码,这是它与 JSON/XML 最大的不同。
syntax = "proto3";
message User {
int32 id = 1;
string name = 2;
int32 age = 3;
repeated string hobbies = 4;
}
需先安装 Protobuf 编译器(protoc),执行以下命令生成 C++ 头文件和源文件:
protoc --cpp_out=. user.proto
执行后会生成 user.pb.h 和 user.pb.cc,包含自动生成的 C++ 类和方法。
(2)JSON:纯文本键值对
JSON 无需预定义,直接通过键值对描述数据,语法规则简单:
- 键必须用双引号包裹
- 值支持字符串、数字、布尔、数组、对象、null
- 数组用
[],对象用 {}
{"id":1001,"name":"Zhang San","age":25,"hobbies":["coding","reading","running"]}
(3)XML:标签化结构化
XML 依赖标签描述数据,每个数据项对应一对闭合标签,支持自定义标签名:
<User>
<id>1001</id>
<name>Zhang San</name>
<age>25</age>
<hobbies>
<hobby>coding</hobby>
<hobby>reading</hobby>
<hobby>running</hobby>
</hobbies>
</User>
三、C++ 实战:序列化与反序列化
前置准备
- Protobuf:需链接 Protobuf 库(编译时加
-lprotobuf)
- JSON:推荐使用开源库
nlohmann/json(单头文件,无需编译)
- XML:推荐使用开源库
tinyxml2(轻量级,易集成)
1. Protobuf 实现(C++)
将 user.pb.h、user.pb.cc 加入项目,包含头文件:
#include "user.pb.h"
#include <iostream>
#include <fstream>
using namespace std;
int main() {
User user;
user.set_id(1001);
user.set_name("Zhang San");
user.set_age(25);
user.add_hobbies("coding");
user.add_hobbies("reading");
user.add_hobbies("running");
fstream output("user.pb", ios::out | ios::binary);
if (!user.SerializeToOstream(&output)) {
cerr << "序列化失败!" << endl;
return -1;
}
output.close();
User user2;
fstream input("user.pb", ios::in | ios::binary);
if (!user2.ParseFromIstream(&input)) {
cerr << "反序列化失败!" << endl;
return -1;
}
input.close();
cout << "Protobuf 反序列化结果:" << endl;
cout << "ID: " << user2.id() << endl;
cout << "Name: " << user2.name() << endl;
cout << "Age: " << user2.age() << endl;
cout << "Hobbies: ";
for (int i = 0; i < user2.hobbies_size(); ++i) {
cout << user2.hobbies(i) << " ";
}
cout << endl;
return 0;
}
g++ main.cpp user.pb.cc -o proto_demo -lprotobuf -std=c++11
- Protobuf 自动生成的
User 类包含 set_xxx()、add_xxx()(数组)、xxx()(获取值)等方法;
SerializeToOstream() 实现序列化(二进制),ParseFromIstream() 实现反序列化;
- 二进制格式不可读,但体积极小,传输 / 存储效率高。
2. JSON 实现(C++,基于 nlohmann/json)
步骤 1:引入 nlohmann/json 头文件
#include "json.hpp"
#include <iostream>
#include <fstream>
using json = nlohmann::json;
using namespace std;
int main() {
json user_json;
user_json["id"] = 1001;
user_json["name"] = "Zhang San";
user_json["age"] = 25;
user_json["hobbies"] = {"coding", "reading", "running"};
ofstream output("user.json");
output << user_json.dump(4);
output.close();
ifstream input("user.json");
json user_json2;
input >> user_json2;
input.close();
cout << "JSON 反序列化结果:" << endl;
cout << "ID: " << user_json2["id"] << endl;
cout << "Name: " << user_json2["name"] << endl;
cout << "Age: " << user_json2["age"] << endl;
cout << "Hobbies: ";
for (auto& hobby : user_json2["hobbies"]) {
cout << hobby << " ";
}
cout << endl;
return 0;
}
g++ main.cpp -o json_demo -std=c++11
nlohmann/json 支持像原生 C++ 对象一样操作 JSON,语法简洁;
dump() 方法将 JSON 对象转为字符串,支持格式化;
- 文本格式可读,解析效率高于 XML,但略低于 Protobuf。
3. XML 实现(C++,基于 tinyxml2)
下载 tinyxml2 的头文件和源文件,加入项目:
#include "tinyxml2.h"
#include <iostream>
#include <fstream>
using namespace tinyxml2;
using namespace std;
int main() {
XMLDocument doc;
XMLElement* root = doc.NewElement("User");
doc.InsertFirstChild(root);
XMLElement* id_node = doc.NewElement("id");
id_node->SetText(1001);
root->InsertEndChild(id_node);
XMLElement* name_node = doc.NewElement("name");
name_node->SetText("Zhang San");
root->InsertEndChild(name_node);
XMLElement* age_node = doc.NewElement("age");
age_node->SetText(25);
root->InsertEndChild(age_node);
XMLElement* hobbies_node = doc.NewElement("hobbies");
root->InsertEndChild(hobbies_node);
string hobbies[] = {"coding", "reading", "running"};
for (auto& hobby : hobbies) {
XMLElement* hobby_node = doc.NewElement("hobby");
hobby_node->SetText(hobby.c_str());
hobbies_node->InsertEndChild(hobby_node);
}
doc.SaveFile("user.xml");
XMLDocument doc2;
XMLError err = doc2.LoadFile("user.xml");
if (err != XML_SUCCESS) {
cerr << "XML 解析失败!" << endl;
return -1;
}
XMLElement* root2 = doc2.FirstChildElement("User");
if (!root2) {
cerr << "未找到根节点!" << endl;
return -1;
}
cout << "XML 反序列化结果:" << endl;
cout << "ID: " << root2->FirstChildElement("id")->GetText() << endl;
cout << "Name: " << root2->FirstChildElement("name")->GetText() << endl;
cout << "Age: " << root2->FirstChildElement("age")->GetText() << endl;
cout << "Hobbies: ";
XMLElement* hobby_node = root2->FirstChildElement("hobbies")->FirstChildElement("hobby");
while (hobby_node) {
cout << hobby_node->GetText() << " ";
hobby_node = hobby_node->NextSiblingElement("hobby");
}
cout << endl;
return 0;
}
g++ main.cpp tinyxml2.cpp -o xml_demo -std=c++11
- tinyxml2 通过节点(XMLElement)操作 XML,需手动创建 / 解析每个节点;
- 语法严格,标签必须闭合,解析逻辑比 JSON 复杂;
- 文本格式最易读,但体积最大,解析效率最低。
四、核心对比:该用哪一个?
| 特性 | Protobuf | JSON | XML |
|---|
| 数据格式 | 二进制 | 文本(键值对) | 文本(标签化) |
| 可读性 | 无(不可读) | 高(易读) | 高(最易读) |
| 体积 | 极小(压缩率最高) | 较小 | 大(冗余标签多) |
| 解析效率 | 极高 | 中 | 低 |
| 扩展性 | 强(支持版本兼容) | 中(无强类型约束) | 中(需手动兼容) |
| 类型约束 | 强(.proto 定义类型) | 弱(无类型检查) | 弱(无类型检查) |
| 适用场景 | 高性能 RPC、游戏、物联网 | 前后端交互、API 接口 | 配置文件、传统系统集成 |
五、新手建议
- 若做前后端交互、轻量级 API:优先选 JSON,上手快、生态完善;
- 若做高性能服务间通信、海量数据传输:选 Protobuf,兼顾效率和扩展性;
- 若做配置文件、传统系统对接:可选 XML(或 JSON 替代),可读性优先。
总结
- Protobuf 是二进制序列化协议,核心优势是高效、紧凑,适合高性能场景;
- JSON 是轻量级文本格式,易读、易解析,是互联网领域数据交换的主流选择;
- XML 是标记语言,结构化最强、可读性最高,但体积和解析效率劣势明显,多用于传统场景。
三者没有绝对的优劣,核心是根据场景选择:追求性能选 Protobuf,追求易用选 JSON,追求结构化可读性选 XML。对于新手,建议先掌握 JSON,再逐步了解 Protobuf(工业界主流),XML 作为了解即可。
微信扫一扫,关注极客日志
微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
相关免费在线工具
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
- Markdown转HTML
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
- HTML转Markdown
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
- JSON 压缩
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
- JSON美化和格式化
将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online