嵌入式高性能 JSON 处理:nlohmann::json 与 rapidjson 选型及通用用法
·
嵌入式高性能 JSON 处理:nlohmann::json 与 rapidjson 选型及通用用法
一、库概述
1. 整体定位
两者均为C++生态下主流的JSON数据处理库,专注于JSON格式的数据序列化(JSON→字符串)与反序列化(字符串→JSON),支撑跨平台、跨系统的数据交互场景,核心设计理念存在显著差异:
- nlohmann::json:以「开发易用性」为核心导向,API设计贴近C++ STL容器,语法简洁直观,大幅降低JSON操作的学习与使用成本。
- rapidjson:以「极致性能与内存可控性」为核心导向,轻量级零依赖,支持精细化内存管理,在高吞吐、低延迟的场景下具备显著优势。
- 两者均为纯头文件库,无需单独编译生成动态库/静态库,直接引入头文件即可集成使用,具备良好的跨平台兼容性。
2. 共性特征
- 支持JSON核心数据类型(字符串、整数、浮点数、布尔值、对象、数组、空值)的完整操作;
- 支持嵌套JSON结构(对象嵌套对象、对象嵌套数组等);
- 支持JSON与C++基础数据类型的相互转换;
- 无第三方运行时依赖,适配桌面端、嵌入式端等多种运行环境。
二、环境准备(基于CMake构建)
1. 库获取与集成
(1)nlohmann::json 集成
两种通用集成方式,适配不同开发场景:
-
方式1:手动集成
- 从官方仓库下载核心头文件
json.hpp; - 在项目目录下创建
include/nlohmann/目录,将json.hpp放入该目录; - 代码中通过
#include <nlohmann/json.hpp>直接引入。
- 从官方仓库下载核心头文件
-
方式2:包管理器集成
以Ubuntu系统为例:sudo apt-get install nlohmann-json3-dev
(2)rapidjson 集成
两种通用集成方式,与nlohmann::json保持一致适配逻辑:
-
方式1:手动集成
- 从官方仓库下载源码包;
- 提取源码中的
include/rapidjson/目录,拷贝到项目的include/目录下; - 代码中通过
#include <rapidjson/document.h>等头文件引入对应模块。
-
方式2:包管理器集成
以Ubuntu系统为例:sudo apt-get install rapidjson-dev
2. CMakeLists.txt 通用配置
以下配置为通用模板,适配手动集成与包管理器集成两种方式,无业务相关依赖,可直接复用:
cmake_minimum_required(VERSION 3.10)
project(JsonGeneralDemo)
# 配置C++标准(nlohmann::json推荐C++17,rapidjson支持C++03及以上,统一指定C++17兼容两者)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 嵌入式场景:静态链接(避免依赖动态库)
set(CMAKE_LINK_STATIC_LIBS ON)
set(CMAKE_EXE_LINKER_FLAGS "-static")
# 1. 配置头文件搜索目录(手动集成时,指定项目内头文件路径)
include_directories(
${PROJECT_SOURCE_DIR}/include # 对应手动拷贝的nlohmann/rapidjson头文件目录
${PROJECT_SOURCE_DIR}/src
)
# 2. (可选)包管理器集成时,自动查找库(本地开发环境适用)
find_package(nlohmann_json 3.0.0 REQUIRED)
find_package(RapidJSON REQUIRED)
include_directories(${RAPIDJSON_INCLUDE_DIRS})
# 3. 构建可执行文件(替换为通用源码文件,无业务关联)
add_executable(JsonGeneralDemo
src/main.cpp
)
# 4. (可选)包管理器集成时,绑定库依赖(仅头文件依赖,无二进制链接)
target_link_libraries(JsonGeneralDemo PRIVATE nlohmann_json::nlohmann_json)
# 嵌入式专属编译优化(针对ARM等架构)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -ffunction-sections -fdata-sections")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") # 移除未使用的函数/数据段,减小文件体积
3. 交叉编译通用适配
针对嵌入式Linux等交叉编译场景,无需额外编译库文件,通用适配步骤如下:
- 将
nlohmann/与rapidjson/头文件目录拷贝到交叉编译工具链的sysroot/usr/include/目录下;
示例(arm-linux-gnueabihf工具链):# 拷贝头文件到工具链目录 cp -r nlohmann/ /opt/arm-linux-gnueabihf/sysroot/usr/include/ cp -r rapidjson/ /opt/arm-linux-gnueabihf/sysroot/usr/include/ - 在CMake工具链配置文件(如
toolchain_arm_linux.cmake)中,确保C_INCLUDE_PATH与CXX_INCLUDE_PATH包含头文件所在目录;
工具链文件示例片段:set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR arm) set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc) set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++) # 指定头文件目录 set(C_INCLUDE_PATH /opt/arm-linux-gnueabihf/sysroot/usr/include) set(CXX_INCLUDE_PATH /opt/arm-linux-gnueabihf/sysroot/usr/include) - 保持编译选项与本地一致,无需额外添加链接参数(纯头文件库无二进制依赖)。
三、核心通用用法
1. 基本操作:JSON对象的创建与取值
(1)nlohmann::json 通用用法
#include <nlohmann/json.hpp>
#include <string>
#include <iostream>
// 命名空间别名(通用简化写法)
namespace json = nlohmann;
void nlohmann_json_basic_operation() {
// 1. 创建空JSON对象
json::json json_obj;
// 2. 添加各类类型成员(直接赋值,类似STL map)
json_obj["string_key"] = "hello world";
json_obj["int_key"] = 100;
json_obj["double_key"] = 3.14159;
json_obj["bool_key"] = true;
json_obj["null_key"] = nullptr;
// 3. 嵌套JSON对象创建
json_obj["nested_obj"]["key1"] = "nested value";
json_obj["nested_obj"]["key2"] = 200;
// 4. 取值操作(直接索引,自动类型转换)
std::string str_val = json_obj["string_key"];
int int_val = json_obj["int_key"];
double double_val = json_obj["double_key"];
bool bool_val = json_obj["bool_key"];
// 5. 类型判断(可选,提高代码健壮性)
if (json_obj["string_key"].is_string()) {
std::cout << "string_key is string type" << std::endl;
}
if (json_obj["nested_obj"].is_object()) {
std::cout << "nested_obj is object type" << std::endl;
}
// 6. 成员存在性判断
if (json_obj.contains("int_key")) {
std::cout << "int_key exists, value: " << json_obj["int_key"] << std::endl;
}
}
(2)rapidjson 通用用法
#include <rapidjson/document.h>
#include <rapidjson/writer.h>
#include <rapidjson/stringbuffer.h>
#include <string>
#include <iostream>
// 命名空间别名(通用简化写法)
namespace rapid = rapidjson;
void rapidjson_basic_operation() {
// 1. 创建空JSON对象,获取内存分配器
rapid::Document doc(rapid::kObjectType);
rapid::Document::AllocatorType& allocator = doc.GetAllocator();
// 2. 添加各类类型成员(需通过rapid::Value构建值对象)
// 字符串类型:需指定分配器,使用Move语义减少拷贝
doc.AddMember("string_key", rapid::Value("hello world", allocator).Move(), allocator);
// 数值类型:直接赋值
doc.AddMember("int_key", 100, allocator);
doc.AddMember("double_key", 3.14159, allocator);
// 布尔类型
doc.AddMember("bool_key", true, allocator);
// 空值类型
doc.AddMember("null_key", rapid::Value(rapid::kNullType).Move(), allocator);
// 嵌入式场景:使用常量字符串(无需Allocator分配内存,减少堆操作)
rapid::Value const_str_val(rapid::kStringType);
const_str_val.SetString("constant_str", rapid::StringRef("constant_str").Length());
doc.AddMember("const_key", const_str_val.Move(), allocator);
// 3. 嵌套JSON对象创建
rapid::Value nested_obj(rapid::kObjectType);
nested_obj.AddMember("key1", rapid::Value("nested value", allocator).Move(), allocator);
nested_obj.AddMember("key2", 200, allocator);
doc.AddMember("nested_obj", nested_obj.Move(), allocator);
// 4. 取值操作(先判断成员存在性与类型,避免程序崩溃)
if (doc.HasMember("string_key") && doc["string_key"].IsString()) {
std::string str_val = doc["string_key"].GetString();
std::cout << "string_key value: " << str_val << std::endl;
}
if (doc.HasMember("int_key") && doc["int_key"].IsInt()) {
int int_val = doc["int_key"].GetInt();
std::cout << "int_key value: " << int_val << std::endl;
}
if (doc.HasMember("double_key") && doc["double_key"].IsDouble()) {
double double_val = doc["double_key"].GetDouble();
std::cout << "double_key value: " << double_val << std::endl;
}
if (doc.HasMember("nested_obj") && doc["nested_obj"].IsObject()) {
const rapid::Value& nested = doc["nested_obj"];
if (nested.HasMember("key1") && nested["key1"].IsString()) {
std::string nested_val = nested["key1"].GetString();
std::cout << "nested_obj.key1 value: " << nested_val << std::endl;
}
}
}
2. 序列化与反序列化:JSON ↔ 字符串
(1)nlohmann::json 通用用法
#include <nlohmann/json.hpp>
#include <string>
#include <iostream>
namespace json = nlohmann;
void nlohmann_json_serialize_deserialize() {
// 1. JSON对象序列化为字符串
json::json json_obj;
json_obj["key1"] = "value1";
json_obj["key2"] = 123;
// 紧凑格式(无空格、无换行,适合嵌入式存储/传输)
std::string compact_str = json_obj.dump();
// 格式化格式(带缩进,适合调试/日志输出)
std::string pretty_str = json_obj.dump(4); // 4个空格缩进
std::cout << "Compact JSON string: " << compact_str << std::endl;
std::cout << "Pretty JSON string: " << pretty_str << std::endl;
// 2. 字符串反序列化为JSON对象
std::string raw_json = R"({"name":"test","age":20})";
try {
// 解析字符串
json::json parsed_json = json::json::parse(raw_json);
// 取值使用
std::string name = parsed_json["name"];
int age = parsed_json["age"];
std::cout << "Parsed name: " << name << ", age: " << age << std::endl;
} catch (const json::json::parse_error& e) {
// 捕获解析异常
std::cerr << "JSON parse error: " << e.what() << std::endl;
}
}
(2)rapidjson 通用用法
#include <rapidjson/document.h>
#include <rapidjson/writer.h>
#include <rapidjson/stringbuffer.h>
#include <rapidjson/prettywriter.h>
#include <string>
#include <iostream>
namespace rapid = rapidjson;
void rapidjson_serialize_deserialize() {
// 1. JSON对象序列化为字符串
rapid::Document doc(rapid::kObjectType);
rapid::Document::AllocatorType& allocator = doc.GetAllocator();
doc.AddMember("key1", rapid::Value("value1", allocator).Move(), allocator);
doc.AddMember("key2", 123, allocator);
// 紧凑格式(无空格、无换行,适合嵌入式存储/传输)
rapid::StringBuffer compact_buffer;
rapid::Writer<rapid::StringBuffer> compact_writer(compact_buffer);
doc.Accept(compact_writer);
std::string compact_str = compact_buffer.GetString();
// 格式化格式(带缩进)
rapid::StringBuffer pretty_buffer;
rapid::PrettyWriter<rapid::StringBuffer> pretty_writer(pretty_buffer);
doc.Accept(pretty_writer);
std::string pretty_str = pretty_buffer.GetString();
std::cout << "Compact JSON string: " << compact_str << std::endl;
std::cout << "Pretty JSON string: " << pretty_str << std::endl;
// 2. 字符串反序列化为JSON对象
std::string raw_json = R"({"name":"test","age":20})";
rapid::Document parsed_doc;
// 解析字符串,判断是否解析失败
if (parsed_doc.Parse(raw_json.c_str()).HasParseError()) {
std::cerr << "JSON parse error, code: " << parsed_doc.GetParseError() << std::endl;
return;
}
// 取值使用
if (parsed_doc.HasMember("name") && parsed_doc["name"].IsString()) {
std::string name = parsed_doc["name"].GetString();
std::cout << "Parsed name: " << name << std::endl;
}
if (parsed_doc.HasMember("age") && parsed_doc["age"].IsInt()) {
int age = parsed_doc["age"].GetInt();
std::cout << "Parsed age: " << age << std::endl;
}
}
3. 通用操作:JSON数组
(1)nlohmann::json 通用用法
#include <nlohmann/json.hpp>
#include <string>
#include <iostream>
#include <vector>
namespace json = nlohmann;
void nlohmann_json_array_operation() {
// 1. 创建JSON数组
json::json json_array;
// 2. 添加元素(支持多种类型)
json_array.push_back("element1");
json_array.push_back(456);
json_array.push_back(2.718);
json_array.push_back(false);
// 数组中嵌套对象
json::json nested_obj;
nested_obj["nested_key"] = "nested_val";
json_array.push_back(nested_obj);
// 3. 遍历数组(支持范围for循环,类似STL容器)
for (size_t i = 0; i < json_array.size(); ++i) {
std::cout << "Array index " << i << ": " << json_array[i] << std::endl;
}
// 范围for遍历
for (const auto& elem : json_array) {
std::cout << "Array element: " << elem << std::endl;
}
// 4. 取值操作
std::string first_elem = json_array[0];
int second_elem = json_array[1];
std::cout << "First element: " << first_elem << std::endl;
std::cout << "Second element: " << second_elem << std::endl;
// 5. 数组与C++ vector互转
std::vector<int> int_vec = {1,2,3,4,5};
// vector → JSON数组
json::json vec_to_json = int_vec;
// JSON数组 → vector
std::vector<int> json_to_vec = vec_to_json.get<std::vector<int>>();
}
(2)rapidjson 通用用法
#include <rapidjson/document.h>
#include <rapidjson/stringbuffer.h>
#include <rapidjson/writer.h>
#include <string>
#include <iostream>
#include <vector>
namespace rapid = rapidjson;
void rapidjson_array_operation() {
// 1. 创建JSON数组
rapid::Document doc(rapid::kArrayType);
rapid::Document::AllocatorType& allocator = doc.GetAllocator();
// 2. 添加元素(支持多种类型,使用PushBack + Move语义)
doc.PushBack(rapid::Value("element1", allocator).Move(), allocator);
doc.PushBack(456, allocator);
doc.PushBack(2.718, allocator);
doc.PushBack(false, allocator);
// 数组中嵌套对象
rapid::Value nested_obj(rapid::kObjectType);
nested_obj.AddMember("nested_key", rapid::Value("nested_val", allocator).Move(), allocator);
doc.PushBack(nested_obj.Move(), allocator);
// 3. 遍历数组(使用SizeType索引,无范围for原生支持)
for (rapid::SizeType i = 0; i < doc.Size(); ++i) {
const rapid::Value& elem = doc[i];
std::cout << "Array index " << i << ": ";
if (elem.IsString()) {
std::cout << elem.GetString() << std::endl;
} else if (elem.IsInt()) {
std::cout << elem.GetInt() << std::endl;
} else if (elem.IsDouble()) {
std::cout << elem.GetDouble() << std::endl;
} else if (elem.IsBool()) {
std::cout << (elem.GetBool() ? "true" : "false") << std::endl;
} else if (elem.IsObject()) {
std::cout << "nested object" << std::endl;
}
}
// 4. 取值操作(先判断类型)
if (doc.Size() > 0 && doc[0].IsString()) {
std::string first_elem = doc[0].GetString();
std::cout << "First element: " << first_elem << std::endl;
}
if (doc.Size() > 1 && doc[1].IsInt()) {
int second_elem = doc[1].GetInt();
std::cout << "Second element: " << second_elem << std::endl;
}
// 5. 数组与C++ vector互转(手动遍历实现)
std::vector<int> int_vec = {1,2,3,4,5};
// vector → rapidjson数组
rapid::Value vec_to_array(rapid::kArrayType);
for (int val : int_vec) {
vec_to_array.PushBack(val, allocator);
}
// rapidjson数组 → vector
std::vector<int> array_to_vec;
if (vec_to_array.IsArray()) {
for (rapid::SizeType i = 0; i < vec_to_array.Size(); ++i) {
if (vec_to_array[i].IsInt()) {
array_to_vec.push_back(vec_to_array[i].GetInt());
}
}
}
}
4. 通用结构体与JSON互转
(1)nlohmann::json 通用用法(支持自动/手动互转)
#include <nlohmann/json.hpp>
#include <string>
namespace json = nlohmann;
// 通用测试结构体
struct CommonStruct {
std::string str_member;
int int_member;
double double_member;
bool bool_member;
};
// 方式1:手动互转(通用灵活,精准控制字段)
void struct_to_nlohmann_json(json::json& j, const CommonStruct& s) {
j["str_member"] = s.str_member;
j["int_member"] = s.int_member;
j["double_member"] = s.double_member;
j["bool_member"] = s.bool_member;
}
void nlohmann_json_to_struct(const json::json& j, CommonStruct& s) {
if (j.contains("str_member")) {
s.str_member = j["str_member"];
}
if (j.contains("int_member")) {
s.int_member = j["int_member"];
}
if (j.contains("double_member")) {
s.double_member = j["double_member"];
}
if (j.contains("bool_member")) {
s.bool_member = j["bool_member"];
}
}
// 方式2:自动互转(简洁高效,需启用宏)
// 宏参数:结构体类型 + 成员变量列表(需与JSON字段名一致)
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CommonStruct, str_member, int_member, double_member, bool_member)
void nlohmann_json_struct_convert() {
CommonStruct s = {"test", 100, 3.14, true};
// 手动互转
json::json j1;
struct_to_nlohmann_json(j1, s);
CommonStruct s1;
nlohmann_json_to_struct(j1, s1);
// 自动互转
json::json j2 = s; // 结构体 → JSON
CommonStruct s2 = j2.get<CommonStruct>(); // JSON → 结构体
}
(2)rapidjson 通用用法(仅支持手动互转)
#include <rapidjson/document.h>
#include <string>
namespace rapid = rapidjson;
// 通用测试结构体
struct CommonStruct {
std::string str_member;
int int_member;
double double_member;
bool bool_member;
};
// 结构体 → rapidjson Value
void struct_to_rapidjson_value(const CommonStruct& s, rapid::Value& val, rapid::Document::AllocatorType& allocator) {
val.SetObject();
val.AddMember("str_member", rapid::Value(s.str_member.c_str(), allocator).Move(), allocator);
val.AddMember("int_member", s.int_member, allocator);
val.AddMember("double_member", s.double_member, allocator);
val.AddMember("bool_member", s.bool_member, allocator);
}
// rapidjson Value → 结构体
void rapidjson_value_to_struct(const rapid::Value& val, CommonStruct& s) {
if (val.HasMember("str_member") && val["str_member"].IsString()) {
s.str_member = val["str_member"].GetString();
}
if (val.HasMember("int_member") && val["int_member"].IsInt()) {
s.int_member = val["int_member"].GetInt();
}
if (val.HasMember("double_member") && val["double_member"].IsDouble()) {
s.double_member = val["double_member"].GetDouble();
}
if (val.HasMember("bool_member") && val["bool_member"].IsBool()) {
s.bool_member = val["bool_member"].GetBool();
}
}
void rapidjson_struct_convert() {
CommonStruct s = {"test", 100, 3.14, true};
rapid::Document doc(rapid::kObjectType);
rapid::Document::AllocatorType& allocator = doc.GetAllocator();
// 结构体 → rapidjson Value
rapid::Value val(rapid::kObjectType);
struct_to_rapidjson_value(s, val, allocator);
doc.AddMember("struct_data", val.Move(), allocator);
// rapidjson Value → 结构体
CommonStruct s_parse;
if (doc.HasMember("struct_data") && doc["struct_data"].IsObject()) {
rapidjson_value_to_struct(doc["struct_data"], s_parse);
}
}
四、核心异同点对比
| 对比维度 | nlohmann::json | rapidjson |
|---|---|---|
| 设计核心 | 易用性优先,贴近STL语法 | 性能优先,精细化内存管理 |
| API风格 | 简洁直观,支持直接赋值/取值 | 手动管控,依赖Value/Allocator对象 |
| 性能表现 | 优秀,满足绝大多数通用场景 | 极致性能,内存拷贝少,吞吐率更高 |
| 内存占用 | 略高(自动内存管理,存在冗余开销) | 极低(手动内存管理,无冗余开销) |
| 结构体互转 | 支持自动/手动互转,灵活可选 | 仅支持手动互转,精准可控 |
| 异常处理 | 解析失败抛出C++异常,需try-catch捕获 | 解析失败返回错误码,无异常抛出 |
| 格式化输出 | 内置dump(n)方法,直接生成格式化字符串 | 需使用PrettyWriter类,额外配置 |
| C++标准兼容 | 支持C++11及以上,推荐C++17 | 支持C++03及以上,兼容性更强 |
| 学习成本 | 低,无需记忆复杂API,快速上手 | 较高,需理解内存分配器、Move语义等 |
| 功能冗余度 | 高,内置多种便捷工具方法 | 低,仅保留核心功能,轻量级 |
| 嵌入式适配性 | 良好,可通过宏裁剪功能 | 极佳,支持内存池、无堆内存依赖 |
共性总结
- 均为纯头文件库,无需编译二进制库,跨平台兼容性强;
- 均支持JSON核心数据类型与嵌套结构的完整操作;
- 均支持JSON与字符串的序列化/反序列化;
- 均支持手动实现结构体与JSON的相互转换;
- 无第三方运行时依赖,适配桌面端、嵌入式端等场景。
五、通用注意事项
1. 异常处理
- nlohmann::json 解析无效JSON、类型转换失败时会抛出异常,生产环境中需使用
try-catch捕获对应异常类型:try { auto j = json::json::parse("invalid json"); } catch (const json::json::exception& e) { // 捕获所有nlohmann::json相关异常 std::cerr << "JSON error: " << e.what() << std::endl; } - rapidjson 无异常抛出,通过
HasParseError()判断解析状态,通过HasMember()+类型判断避免取值崩溃,更适合高可靠性场景:rapid::Document doc; if (doc.Parse("invalid json").HasParseError()) { std::cerr << "Parse error at offset " << doc.GetErrorOffset() << std::endl; }
2. 内存管理
- nlohmann::json 自动管理内存,无需手动干预,可使用
swap()方法减少大数据量场景下的内存拷贝; - rapidjson 推荐使用
Move()语义(Value::Move())传递对象,避免不必要的内存拷贝,提升性能; - 两者在嵌入式内存受限场景下均适用,rapidjson的内存占用优势更为显著。
3. 字符串处理
- nlohmann::json 支持C++
std::string直接赋值与取值,无需额外内存管理; - rapidjson 字符串值需依赖
Allocator分配内存,推荐使用rapid::Value(str, allocator).Move()的方式构建,避免内存泄漏; - 嵌入式场景下,优先使用
rapid::StringRef处理常量字符串,减少堆内存分配操作。
4. 类型安全
- 两者均需保证JSON字段类型与取值类型一致,nlohmann::json 类型不匹配时会抛出异常,rapidjson 类型不匹配时会导致未定义行为;
- 生产环境中,无论使用哪个库,均建议先进行类型判断,再执行取值操作,提高代码健壮性。
5. 编译优化
- 两者均可通过CMake配置编译优化选项(如
-O2),进一步提升运行性能; - rapidjson 可通过定义宏(如
RAPIDJSON_HAS_STDSTRING)开启std::string兼容支持,简化字符串操作。
六、嵌入式场景专属选型建议
-
优先选 rapidjson 的场景
- 实时性要求高:如传感器数据实时序列化(雷达、摄像头)、通信协议(TCP/UDP/WebSocket)高频数据传输;
- 内存/存储受限:如小型嵌入式设备(MCU、低端ARM板)、无操作系统(RTOS)环境;
- 高可靠性要求:如工业控制、车载设备,需避免异常抛出,依赖错误码处理故障。
-
优先选 nlohmann::json 的场景
- 开发效率优先:如嵌入式设备的配置解析(Flash/配置文件读取)、日志记录,无需极致性能;
- 复杂数据结构:如嵌套多层的配置对象、需要与STL容器快速互转,减少手动代码编写;
- 桌面端与嵌入式共用代码:一套代码适配桌面调试与嵌入式部署,降低移植成本。
-
混合使用场景
- 嵌入式设备中,「配置解析」使用 nlohmann::json(开发高效),「实时数据传输」使用 rapidjson(性能优先),兼顾开发效率与运行性能。
更多推荐


所有评论(0)