jsoncpp
- 1. JSON数据
- 1.1 JSON 的基本语法规则
- 1. 基础语法要求
- 两种核心数据结构
- JSON 与其他数据格式的对比
- 1.2 JSON 的典型应用场景
- 1.3 JSON 解析与生成工具
- 2. 编程语言库(解析/生成)
- 1.4 常见错误与注意事项
- 2. jsoncpp
- 2.1 基本用法
- 1. 安装与集成
- 2. 核心类与命名空间
- 3. 解析 JSON 字符串**
- 4. 生成 JSON 字符串
- 5. 读写 JSON 文件
- 2.2 主要类和函数
- 核心类
- 1. `Json::Value`
- 2. `Json::Reader`(旧版解析器)
- 3. `Json::CharReader`(新版解析器)
- 4. `Json::Writer` 及其派生类
- 5. `Json::CharReaderBuilder` 与 `Json::StreamWriterBuilder`
- 辅助函数与工具
- 1. 类型转换函数
- 2. 文件操作辅助
- 核心类关系与工作流程
- 总结
- 2.3 高级特性
- 2.4 总结
- 2.5 示例
1. JSON数据
JSON(JavaScript Object Notation,JavaScript 对象表示法)是一种轻量级、文本格式的通用数据交换格式,旨在简化数据的存储、传输和解析。它基于 JavaScript 的对象语法,但独立于编程语言,几乎所有主流语言(如 C++、Python、Java、JavaScript)都提供原生或第三方库支持。
- 简洁易读:采用人类可直接理解的文本结构,比 XML 更简洁(无冗余标签)。
- 跨平台/跨语言:不依赖特定编程语言,是不同系统间数据交换的“通用语言”(如前后端通信、服务间调用)。
- 轻量级:相同数据的 JSON 体积远小于 XML,传输效率更高。
- 支持常用数据类型:覆盖字符串、数字、布尔、数组、对象等基础数据结构,满足大多数场景需求。
1.1 JSON 的基本语法规则
JSON 数据由两种核心结构组成:键值对集合(对象) 和 有序值列表(数组),所有语法需严格遵循以下规则:
1. 基础语法要求
- 键名必须用 双引号
"
包裹(不能用单引号或无引号)。 - 值可以是字符串、数字、布尔、
null
、对象或数组。 - 键值对之间用 逗号
,
分隔(最后一个键值对后不能加逗号,否则会解析错误)。 - 字符串必须用双引号包裹,支持转义字符(如
\n
换行、\"
双引号、\\
反斜杠)。
两种核心数据结构
- 对象(Object):键值对的集合
用于表示“实体”(如用户、配置项),格式为{ "键1": 值1, "键2": 值2, ... }
。
示例(用户信息):
{"name": "Alice", // 字符串值"age": 25, // 数字值(整数/浮点数均可,无需引号)"isStudent": false, // 布尔值(true/false,无引号)"email": null, // 空值(null,无引号)"address": { // 嵌套对象(值为另一个 JSON 对象)"city": "Beijing","street": "Main Street","zipCode": "100000"}
}
- 数组(Array):有序的值列表
用于表示“多个同类型/相关值的集合”(如列表、数组),格式为[ 值1, 值2, ... ]
。
示例(用户列表/爱好):
// 数组作为顶层结构(用户列表)
[{ "name": "Alice", "age": 25 },{ "name": "Bob", "age": 30 }
]// 数组作为对象的值(用户爱好)
{"name": "Charlie","hobbies": ["reading", "coding", "hiking"] // 字符串数组
}
- 常见数据类型及示例
数据类型 | 语法规则 | 示例 |
---|---|---|
字符串 | 双引号包裹,支持转义 | "name": "Alice" , "intro": "I'm a developer\nLove coding" |
数字 | 整数/浮点数,无引号,支持负数和科学计数法 | "age": 25 , "score": 98.5 , "value": -100 , "sci": 1e5 |
布尔 | 仅 true 或 false (无引号) | "isStudent": true |
null | 表示“无值”(无引号) | "email": null |
对象 | {} 包裹的键值对集合,可嵌套 | "address": { "city": "Shanghai" } |
数组 | [] 包裹的有序值列表,值类型可混合 | "tags": [1, "important", false] |
JSON 与其他数据格式的对比
格式 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
JSON | 简洁、轻量、跨语言支持好、易解析 | 不支持注释(需通过工具扩展)、不适合复杂文档结构 | 前后端通信、API 接口、配置文件、轻量级数据交换 |
XML | 支持注释、命名空间、复杂文档结构(如嵌套层级深的配置) | 冗余标签多、体积大、解析效率低 | 传统企业级应用(如 SOAP 接口)、配置文件(如 Spring) |
YAML | 语法更简洁(无引号/括号,用缩进表示层级)、支持注释 | 对缩进敏感(易因格式错误导致解析失败) | 配置文件(如 Docker、K8s、Python 项目) |
1.2 JSON 的典型应用场景
-
前后端数据通信:前端(如 JavaScript)通过 AJAX/fetch 向后端(如 Java/C++)发送 JSON 请求,后端返回 JSON 响应(最常见场景)。
示例(后端接口响应):{"code": 200, // 状态码(200=成功,404=未找到)"message": "success","data": { // 业务数据"userList": [{"name": "Alice"}, {"name": "Bob"}],"total": 2} }
-
配置文件:用 JSON 存储应用程序的配置(如窗口大小、数据库连接信息),比 ini 文件支持更复杂的结构。
示例(应用配置):{"appName": "MyEditor","window": {"width": 1024,"height": 768,"fullscreen": false},"database": {"host": "localhost","port": 3306,"username": "root","password": "123456"} }
-
数据存储:小型应用用 JSON 文件存储数据(如用户列表、日志),无需依赖数据库。
示例(用户数据文件users.json
):[{"id": 1, "name": "Alice", "age": 25},{"id": 2, "name": "Bob", "age": 30} ]
-
API 接口规范:绝大多数现代 API(如 RESTful API)采用 JSON 作为数据交换格式(如 GitHub API、微信支付 API)。
1.3 JSON 解析与生成工具
- 在线工具(验证/格式化)
- JSON.cn:在线解析、格式化、验证 JSON(中文友好)。
- JSON Validator:检查 JSON 语法错误,格式化代码。
2. 编程语言库(解析/生成)
- C++:
jsoncpp
、nlohmann/json
(单头文件,更简洁)、rapidjson
(高性能)。 - Python:内置
json
模块(无需额外安装)。 - JavaScript:内置
JSON.parse()
(字符串转对象)和JSON.stringify()
(对象转字符串)。 - Java:
Gson
(Google)、Jackson
、Fastjson
(阿里)。
1.4 常见错误与注意事项
- 键名未用双引号:错误写法
{ name: "Alice" }
→ 正确写法{ "name": "Alice" }
。 - 多余的逗号:错误写法
{ "name": "Alice", "age": 25, }
→ 需删除最后一个逗号。 - 字符串用单引号:错误写法
{ "name": 'Alice' }
→ 正确写法{ "name": "Alice" }
。 - 布尔/数字加引号:错误写法
{ "age": "25", "isStudent": "true" }
→ 正确写法{ "age": 25, "isStudent": true }
(否则解析为字符串,而非对应类型)。 - 不支持注释:JSON 标准不允许注释,若需添加注释,需用工具(如
jsonc
扩展)或在解析前手动删除注释。
JSON 是目前最流行的数据交换格式之一,核心优势是简洁、跨平台、易解析。掌握其语法规则和应用场景,是开发中处理数据交换(如前后端通信、配置文件)的基础。实际开发中,需注意语法规范性(避免解析错误),并根据语言选择合适的库(如 C++ 用 jsoncpp
,Python 用内置 json
模块)实现 JSON 的解析与生成。
2. jsoncpp
jsoncpp 是一个轻量级的 C++ 库,用于解析、生成和操作 JSON 数据。它支持 JSON 标准规范,提供了简单易用的 API,广泛应用于需要处理 JSON 格式数据的 C++ 项目中(如网络通信、配置文件解析等)。
- JSON 解析:将 JSON 字符串或文件解析为 C++ 可操作的对象模型。
- JSON 生成:将 C++ 对象模型序列化为 JSON 字符串或写入文件。
- 数据操作:支持增删改查 JSON 中的键值对、数组元素等。
- 跨平台:兼容 Windows、Linux、macOS 等主流操作系统。
2.1 基本用法
1. 安装与集成
- 源码集成
从 jsoncpp 官网 下载源码,将include/json
目录和src/lib_json
下的.cpp
文件添加到项目中。
jsoncpp库的安装、编译和配置
2. 核心类与命名空间
Json::Value
:表示 JSON 中的任何值(对象、数组、字符串、数字等),是最常用的类。Json::Reader
:用于解析 JSON 字符串或文件到Json::Value
。Json::Writer
:用于将Json::Value
序列化为 JSON 字符串(派生类Json::FastWriter
、Json::StyledWriter
分别生成紧凑/格式化的字符串)。- 命名空间:所有类和函数都在
Json
命名空间下。
3. 解析 JSON 字符串**
#include <iostream>
#include <json/json.h>int main() {// JSON 字符串std::string json_str = R"({"name": "Alice","age": 25,"is_student": false,"hobbies": ["reading", "coding"]})";// 解析 JSONJson::Reader reader;Json::Value root;if (!reader.parse(json_str, root)) {std::cerr << "JSON 解析失败: " << reader.getFormattedErrorMessages() << std::endl;return 1;}// 读取数据std::string name = root["name"].asString();int age = root["age"].asInt();bool is_student = root["is_student"].asBool();std::cout << "姓名: " << name << std::endl;std::cout << "年龄: " << age << std::endl;std::cout << "是否学生: " << (is_student ? "是" : "否") << std::endl;// 读取数组std::cout << "爱好: ";for (const auto& hobby : root["hobbies"]) {std::cout << hobby.asString() << " ";}std::cout << std::endl;return 0;
}
4. 生成 JSON 字符串
#include <json/json.h>
#include <iostream>int main() {// 创建 JSON 对象Json::Value root;// 添加键值对root["name"] = "Bob";root["age"] = 30;root["is_employee"] = true;// 添加数组Json::Value skills;skills.append("C++");skills.append("Python");skills.append("Java");root["skills"] = skills;// 生成格式化的 JSON 字符串(带缩进)Json::StyledWriter styled_writer;std::string styled_str = styled_writer.write(root);std::cout << "格式化输出:\n" << styled_str << std::endl;// 生成紧凑的 JSON 字符串(无缩进)Json::FastWriter fast_writer;std::string fast_str = fast_writer.write(root);std::cout << "紧凑输出:\n" << fast_str << std::endl;return 0;
}
输出结果:
格式化输出:
{"age" : 30,"is_employee" : true,"name" : "Bob","skills" : [ "C++", "Python", "Java" ]
}紧凑输出:
{"age":30,"is_employee":true,"name":"Bob","skills":["C++","Python","Java"]}
5. 读写 JSON 文件
#include <json/json.h>
#include <fstream>
#include <iostream>// 写入 JSON 到文件
void writeJsonToFile(const std::string& filename) {Json::Value root;root["config"] = "app_settings";root["window"]["width"] = 800;root["window"]["height"] = 600;std::ofstream ofs(filename);if (ofs.is_open()) {Json::StyledWriter writer;ofs << writer.write(root);ofs.close();}
}// 从文件读取 JSON
void readJsonFromFile(const std::string& filename) {std::ifstream ifs(filename);if (!ifs.is_open()) {std::cerr << "无法打开文件: " << filename << std::endl;return;}Json::Reader reader;Json::Value root;if (reader.parse(ifs, root)) {std::cout << "配置名称: " << root["config"].asString() << std::endl;std::cout << "窗口宽度: " << root["window"]["width"].asInt() << std::endl;std::cout << "窗口高度: " << root["window"]["height"].asInt() << std::endl;} else {std::cerr << "文件解析失败: " << reader.getFormattedErrorMessages() << std::endl;}ifs.close();
}int main() {std::string filename = "config.json";writeJsonToFile(filename);readJsonFromFile(filename);return 0;
}
2.2 主要类和函数
Jsoncpp 库通过几个核心类实现了 JSON 数据的解析、生成和操作,以下是其主要类、函数及其功能的详细说明:
核心类
1. Json::Value
- 功能:表示 JSON 中的任何值(对象、数组、字符串、数字等),是 Jsoncpp 中最基础、最常用的类。
- 特点:
- 可动态存储不同类型的 JSON 数据(自动类型转换)。
- 支持嵌套结构(对象中包含对象或数组)。
- 常用方法:
方法 功能 asString()
转换为字符串(若类型不匹配,返回空字符串) asInt()
/asDouble()
转换为整数/浮点数(类型不匹配返回 0) asBool()
转换为布尔值(类型不匹配返回 false
)isString()
/isInt()
/isBool()
检查当前值的类型 isMember(const std::string& key)
检查对象中是否包含指定键 size()
返回数组或对象的元素数量 append(const Value& value)
向数组添加元素(仅对数组类型有效) clear()
清空当前值(对象/数组的元素会被删除) - 示例:
Json::Value root; root["name"] = "Alice"; // 字符串 root["age"] = 25; // 整数 root["scores"].append(90); // 数组添加元素 root["address"]["city"] = "Beijing"; // 嵌套对象
2. Json::Reader
(旧版解析器)
- 功能:将 JSON 字符串或输入流解析为
Json::Value
对象。 - 注意:Jsoncpp 1.0+ 推荐使用
Json::CharReader
替代(性能更好)。 - 常用方法:
方法 功能 parse(const std::string& json_str, Value& root)
解析 JSON 字符串到 root
parse(std::istream& is, Value& root)
解析输入流(如文件流)到 root
getFormattedErrorMessages()
解析失败时返回错误信息 - 示例:
Json::Reader reader; Json::Value root; std::string json_str = "{\"name\":\"Alice\"}"; if (reader.parse(json_str, root)) {std::string name = root["name"].asString(); } else {std::cerr << "解析错误: " << reader.getFormattedErrorMessages(); }
3. Json::CharReader
(新版解析器)
- 功能:替代
Json::Reader
,提供更高效的 JSON 解析,支持从字符串或流中解析。 - 使用方式:通过
Json::CharReaderBuilder
创建实例(工厂模式)。 - 常用方法:
方法 功能 parse(const char* begin, const char* end, Value* root, std::string* errors)
解析字符范围 [begin, end)
到root
,错误信息存入errors
- 示例:
Json::CharReaderBuilder builder; std::unique_ptr<Json::CharReader> reader(builder.newCharReader()); Json::Value root; std::string errors; const std::string json_str = "{\"age\":25}";if (reader->parse(json_str.data(), json_str.data() + json_str.size(), &root, &errors)) {int age = root["age"].asInt(); } else {std::cerr << "解析错误: " << errors; }
4. Json::Writer
及其派生类
- 功能:将
Json::Value
对象序列化为 JSON 字符串。 - 派生类:
Json::FastWriter
:生成紧凑无格式的 JSON 字符串(无缩进、换行)。Json::StyledWriter
:生成格式化的 JSON 字符串(带缩进、换行,可读性强)。Json::StyledStreamWriter
:直接向输出流(如文件)写入格式化的 JSON。
- 常用方法:
方法 功能 write(const Value& root)
将 root
序列化为 JSON 字符串(FastWriter
/StyledWriter
)write(std::ostream& out, const Value& root)
向输出流写入 JSON 字符串( StyledStreamWriter
) - 示例:
Json::Value root; root["name"] = "Bob";// 紧凑输出 Json::FastWriter fast_writer; std::string fast_str = fast_writer.write(root); // 结果: {"name":"Bob"}// 格式化输出 Json::StyledWriter styled_writer; std::string styled_str = styled_writer.write(root); // 带缩进的格式化字符串
5. Json::CharReaderBuilder
与 Json::StreamWriterBuilder
- 功能:分别用于创建
Json::CharReader
和Json::StreamWriter
的工厂类,支持自定义解析/生成选项(如缩进空格数、错误提示语言)。 - 示例(自定义格式化参数):
Json::StreamWriterBuilder builder; builder["indentation"] = " "; // 设置缩进为 2 个空格 std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());Json::Value root; root["name"] = "Charlie"; writer->write(root, &std::cout); // 向控制台输出格式化 JSON
辅助函数与工具
1. 类型转换函数
Json::ValueType Json::Value::type()
:返回当前值的类型(nullValue
、intValue
、stringValue
等)。bool Json::Value::empty()
:检查值是否为空(空对象、空数组或null
)。
2. 文件操作辅助
- 结合 C++ 标准流(
std::ifstream
/std::ofstream
)实现 JSON 文件的读写:// 从文件读取 JSON std::ifstream ifs("data.json"); Json::Value root; ifs >> root; // 需包含 <json/json.h>,重载了流运算符// 写入 JSON 到文件 std::ofstream ofs("output.json"); ofs << root;
核心类关系与工作流程
- 解析流程:
输入(JSON 字符串/文件) →CharReader
(解析) →Value
(内存对象)。 - 生成流程:
Value
(内存对象) →Writer
(序列化) → 输出(JSON 字符串/文件)。
总结
类/工具 | 核心功能 | 适用场景 |
---|---|---|
Json::Value | 存储和操作 JSON 数据 | 所有需要处理 JSON 的场景(核心) |
Json::CharReader | 解析 JSON 为 Value | 从字符串/文件读取 JSON |
Json::FastWriter | 生成紧凑 JSON 字符串 | 网络传输(体积小) |
Json::StyledWriter | 生成格式化 JSON 字符串 | 配置文件(可读性强) |
CharReaderBuilder /StreamWriterBuilder | 自定义解析/生成参数 | 需要定制 JSON 格式时 |
掌握这些核心类和方法后,即可完成 JSON 数据的解析、生成、修改等基本操作,满足大多数 C++ 项目的 JSON 处理需求。
2.3 高级特性
- 类型检查
使用isXxx()
方法检查 JSON 值的类型,避免访问错误类型导致崩溃:
if (root["age"].isInt()) {int age = root["age"].asInt();
} else {std::cerr << "age 不是整数类型" << std::endl;
}
- 处理可选键
使用isMember()
检查键是否存在:
if (root.isMember("email")) {std::string email = root["email"].asString();
} else {std::cout << "email 键不存在" << std::endl;
}
- 嵌套对象与数组
支持多层嵌套的 JSON 结构:
// 嵌套对象
root["address"]["city"] = "Beijing";
root["address"]["street"] = "Main Street";// 数组操作
root["scores"].append(90);
root["scores"].append(85);
int first_score = root["scores"][0].asInt(); // 获取第一个元素
2.4 总结
- 编码问题:jsoncpp 默认处理 UTF-8 编码,若输入为其他编码(如 GBK),需先转换为 UTF-8。
- 性能考虑:
Json::Reader
解析大型 JSON 时可能较慢,可考虑使用更高效的解析器(如Json::CharReader
):Json::CharReaderBuilder builder; std::unique_ptr<Json::CharReader> reader(builder.newCharReader()); if (!reader->parse(json_str.data(), json_str.data() + json_str.size(), &root, nullptr)) {// 解析失败 }
- 版本差异:jsoncpp 1.0 以上版本与旧版本(0.x)API 有差异,建议使用最新版本。
- 与其他库对比
库 | 特点 | 适用场景 |
---|---|---|
jsoncpp | 经典稳定、API 简单、功能完整 | 大多数 C++ 项目,尤其是需要兼容性的场景 |
nlohmann/json | 单头文件、现代 C++ 风格、易用性强 | 新项目,追求简洁集成 |
rapidjson | 高性能、内存占用低 | 对性能要求高的场景(如服务器) |
jsoncpp 是一个成熟可靠的 JSON 处理库,适合需要在 C++ 中解析或生成 JSON 数据的场景。其 API 设计直观,易于上手,同时支持复杂的 JSON 结构操作。对于大多数项目,jsoncpp 能满足基本需求;若追求更简洁的集成方式,可考虑 nlohmann/json;若对性能有极致要求,可尝试 rapidjson。
2.5 示例
#include <json/json.h>
#include <iostream>
#include <fstream>
#include <string>// 解析 JSON 字符串
void parseJsonString() {std::cout << "=== 解析 JSON 字符串 ===" << std::endl;std::string json_str = R"({"name": "Alice","age": 25,"is_student": false,"hobbies": ["reading", "coding"],"scores": {"math": 90,"english": 85}})";Json::CharReaderBuilder builder;std::unique_ptr<Json::CharReader> reader(builder.newCharReader());Json::Value root;std::string errors;if (reader->parse(json_str.data(), json_str.data() + json_str.size(), &root, &errors)) {std::cout << "姓名: " << root["name"].asString() << std::endl;std::cout << "年龄: " << root["age"].asInt() << std::endl;std::cout << "是否学生: " << (root["is_student"].asBool() ? "是" : "否") << std::endl;std::cout << "爱好: ";for (const auto& hobby : root["hobbies"]) {std::cout << hobby.asString() << " ";}std::cout << std::endl;std::cout << "数学成绩: " << root["scores"]["math"].asInt() << std::endl;} else {std::cerr << "解析失败: " << errors << std::endl;}
}// 生成 JSON 字符串
void generateJsonString() {std::cout << "\n=== 生成 JSON 字符串 ===" << std::endl;Json::Value root;root["name"] = "Bob";root["age"] = 30;root["is_employee"] = true;Json::Value skills;skills.append("C++");skills.append("Python");skills.append("Java");root["skills"] = skills;Json::Value projects;Json::Value project1;project1["name"] = "json_demo";project1["completed"] = true;projects.append(project1);root["projects"] = projects;// 格式化输出Json::StyledWriter styled_writer;std::cout << "格式化 JSON:\n" << styled_writer.write(root) << std::endl;// 紧凑输出Json::FastWriter fast_writer;std::cout << "紧凑 JSON:\n" << fast_writer.write(root) << std::endl;
}// 读写 JSON 文件
void readWriteJsonFile(const std::string& filename) {std::cout << "\n=== 读写 JSON 文件 ===" << std::endl;// 写入文件Json::Value root;root["app_name"] = "MyApp";root["version"] = "1.0.0";root["settings"]["window_width"] = 1024;root["settings"]["window_height"] = 768;root["settings"]["fullscreen"] = false;std::ofstream ofs(filename);if (ofs.is_open()) {Json::StyledWriter writer;ofs << writer.write(root);ofs.close();std::cout << "已写入文件: " << filename << std::endl;} else {std::cerr << "无法写入文件: " << filename << std::endl;return;}// 读取文件std::ifstream ifs(filename);if (ifs.is_open()) {Json::CharReaderBuilder builder;std::unique_ptr<Json::CharReader> reader(builder.newCharReader());Json::Value read_root;std::string errors;if (reader->parse(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>(), &read_root, &errors)) {std::cout << "应用名称: " << read_root["app_name"].asString() << std::endl;std::cout << "版本: " << read_root["version"].asString() << std::endl;std::cout << "窗口宽度: " << read_root["settings"]["window_width"].asInt() << std::endl;} else {std::cerr << "文件解析失败: " << errors << std::endl;}ifs.close();} else {std::cerr << "无法读取文件: " << filename << std::endl;}
}int main() {parseJsonString();generateJsonString();readWriteJsonFile("config.json");return 0;
}