从入门到了解:Protobuf、JSON、XML 核心解析(C++ 示例)

在数据交换和序列化领域,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 最大的不同。
步骤 1:编写 user.proto 文件
syntax = "proto3"; // 指定protobuf版本,推荐proto3
// 定义用户数据结构
message User {
int32 id = 1; // 字段编号(序列化时用,与字段名无关)
string name = 2; // 姓名
int32 age = 3; // 年龄
repeated string hobbies = 4; // repeated表示数组/列表
}
步骤 2:编译生成 C++ 代码
需先安装 Protobuf 编译器(protoc),执行以下命令生成 C++ 头文件和源文件:
protoc --cpp_out=. user.proto
执行后会生成 user.pb.h 和 user.pb.cc,包含自动生成的 C++ 类和方法。
(2)JSON:纯文本键值对
JSON 无需预定义,直接通过键值对描述数据,语法规则简单:
- 键必须用双引号包裹
- 值支持字符串、数字、布尔、数组、对象、null
- 数组用
[],对象用{}
用户信息的 JSON 示例:
{
"id": 1001,
"name": "Zhang San",
"age": 25,
"hobbies": ["coding", "reading", "running"]
}
(3)XML:标签化结构化
XML 依赖标签描述数据,每个数据项对应一对闭合标签,支持自定义标签名:
<User>
<id>1001id>
<name>Zhang Sanname>
<age>25age>
<hobbies>
<hobby>codinghobby>
<hobby>readinghobby>
<hobby>runninghobby>
hobbies>
User>
三、C++ 实战:序列化与反序列化
前置准备
- Protobuf:需链接 Protobuf 库(编译时加
-lprotobuf) - JSON:推荐使用开源库
nlohmann/json(单头文件,无需编译) - XML:推荐使用开源库
tinyxml2(轻量级,易集成)
1. Protobuf 实现(C++)
步骤 1:集成生成的代码
将 user.pb.h、user.pb.cc 加入项目,包含头文件:
#include "user.pb.h"
#include
#include
using namespace std;
int main() {
// 1. 序列化:将数据写入Protobuf对象,再转成二进制
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();
// 2. 反序列化:从二进制文件读取并解析
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 头文件
下载 json.hpp 后,直接包含:
#include "json.hpp"
#include
#include
using json = nlohmann::json;
using namespace std;
int main() {
// 1. 序列化:构造JSON对象,转成字符串
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); // dump(4) 格式化输出,缩进4个空格
output.close();
// 2. 反序列化:从字符串解析为JSON对象
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)
步骤 1:集成 tinyxml2
下载 tinyxml2 的头文件和源文件,加入项目:
#include "tinyxml2.h"
#include
#include
using namespace tinyxml2;
using namespace std;
int main() {
// 1. 序列化:构建XML文档,写入文件
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");
// 2. 反序列化:读取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 作为了解即可。
本文地址:https://www.yitenyun.com/6886.html











