【C++ 脚手架】Jsoncpp 库的介绍与使用
Jsoncpp 的介绍与使用
一、JSON 格式
JSON(JavaScript Object Notation,JavaScript 对象表示法)是一种轻量级的文本数据交换格式。它易于人阅读和编写,也易于机器解析和生成。尽管 JSON 源自 JavaScript,但它是一种与编程语言无关的格式,目前绝大多数现代编程语言都支持它。
1.1 基本的语法规则
-
数据以键值对形式存在:
"key": value。 -
键(Key):必须是双引号包裹的字符串。
-
值(Value):可以是以下类型:
- 字符串:
"hello" - 数字:
100或3.14 - 布尔值:
true或false - 数组:
["apple" , "banana"] - 对象:
{"name": "Alice"} - 空值:
null
- 字符串:
-
数据之间用逗号分隔。
-
不允许在最后一个成员后添加多余的逗号。
1.2 示例
JSON 通常由两种结构嵌套组成:
-
对象(Object):花括号
{}包裹的无序键值对集合。 -
数组(Array):方括号
[]包裹的有序值列表。
{
"name": "张三",
"age": 30,
"isStudent": false,
"hobbies": ["阅读", "编程", "游泳"],
"address": {
"city": "北京",
"zipcode": "100000"
},
"project": null
}
1.3 场景
-
数据传输:前后端交互的主流数据格式。
-
配置文件:用于应用程序的配置信息。
-
数据存储:作为⼀种数据格式,⽤于⽂件和数据库存储。
二、Jsoncpp 使用
2.1 Jsoncpp 介绍
JsonCpp 是一个在 C++ 中处理 JSON 数据的开源库,它提供了一套简单易用的 API,让你可以方便地在 C++ 对象和 JSON 字符串之间进行转换(即序列化与反序列化)。它轻量、跨平台,是 C++ 项目中最常用的 JSON 库之一。
-
序列化(Serialization):将内存中的对象或数据结转换为一种可存储或可传输的格式的过程。
-
反序列化(Deserialization):序列化的逆过程,将存储或传输格式的数据还原为内存中的对象或数据结构。
Jsoncpp 的序列化就是将内存中的对象转换成 JSON 格式的文本字符串,这个字符串是明文(可读的)。
2.2 Jsoncpp 安装
sudo apt-get install libjsoncpp-dev
2.3 头文件包含
#include <jsoncpp/json/json.h>
2.4 程序编译时的链接
g++ main.cc -o main -ljsoncpp
2.5 Json::Value 数据转储对象
Json::Value 是 jsoncpp 库中最核心的类,它代表一个 JSON 数据实体,可以容纳 JSON 规范定义的所有数据类型(null、布尔值、数值、字符串、数组、对象),并提供了统一的访问接口。
-
序列化:通过
Json::StreamWriter将Json::Value转换为 JSON 字符串。 -
反序列化:通过
Json::CharReader将 JSON 字符串解析为Json::Value。
namespace Json {
class JSON_API Value {
public:
/**
* @brief 交换两个 Value 对象的内容(高效,不进行深拷贝)
* @param other 要交换的另一个 Value 对象
*/
void swap(Value& other);
/**
* @brief 将另一个 Value 对象的内容复制到当前对象(深拷贝)
* @param other 源 Value 对象
*/
void copy(const Value& other);
/**
* @brief 将 Value 对象中的元素以什么类型返回
* @note 仅当 Value 为对应类型时,否则可能返回空指针或触发断言
* @return 指向字符串内部缓冲区的指针
*/
const char* asCString() const;
/**
* @brief 将 Value 以 std::string 形式返回
* @return 字符串副本(若 Value 不是字符串类型,则尝试转换)
*/
String asString() const;
/**
* @brief 将 Value 转换为有符号 32 位整数返回
* @note 若 Value 为数值型,会进行截断或舍入;若非数值型,行为未定义(通常返回 0)
* @return 转换后的整数值
*/
Int asInt() const;
/**
* @brief 将 Value 转换为无符号 32 位整数返回
* @return 转换后的整数值
*/
UInt asUInt() const;
/**
* @brief 将 Value 转换为有符号 64 位整数返回
* @return 转换后的整数值
*/
Int64 asInt64() const;
/**
* @brief 将 Value 转换为无符号 64 位整数返回
* @return 转换后的整数值
*/
UInt64 asUInt64() const;
/**
* @brief 将 Value 转换为单精度浮点数返回
* @return 转换后的浮点值
*/
float asFloat() const;
/**
* @brief 将 Value 转换为双精度浮点数返回
* @return 转换后的浮点值
*/
double asDouble() const;
/**
* @brief 将 Value 转换为布尔值返回
* @return 转换后的布尔值(对于非布尔型,根据特定规则转换,如 0→false, 1→true 等)
*/
bool asBool() const;
/**
* @brief 判断当前 Value 是否为 null(即 JSON 中的 null)
* @return true 表示为 null,否则为 false
*/
bool isNull() const;
/**
* @brief 判断当前 Value 是否为布尔类型
* @return true 表示为布尔类型,否则为 false
*/
bool isBool() const;
/**
* @brief 判断当前 Value 是否为 32 位有符号整数类型(或可无损转换为该类型)
* @return true 表示可以安全地作为 Int 使用
*/
bool isInt() const;
/**
* @brief 判断当前 Value 是否为 64 位有符号整数类型(或可无损转换)
* @return true 表示可以安全地作为 Int64 使用
*/
bool isInt64() const;
/**
* @brief 判断当前 Value 是否为 32 位无符号整数类型(或可无损转换)
* @return true 表示可以安全地作为 UInt 使用
*/
bool isUInt() const;
/**
* @brief 判断当前 Value 是否为 64 位无符号整数类型(或可无损转换)
* @return true 表示可以安全地作为 UInt64 使用
*/
bool isUInt64() const;
/**
* @brief 判断当前 Value 是否为整数类型(包括有符号和无符号,不包括浮点)
* @return true 表示为整数类型
*/
bool isIntegral() const;
/**
* @brief 判断当前 Value 是否为双精度浮点数类型(或可无损转换为 double)
* @return true 表示为浮点类型
*/
bool isDouble() const;
/**
* @brief 判断当前 Value 是否为数值类型(整数或浮点数)
* @return true 表示为数值类型
*/
bool isNumeric() const;
/**
* @brief 判断当前 Value 是否为字符串类型
* @return true 表示为字符串类型
*/
bool isString() const;
/**
* @brief 判断当前 Value 是否为数组类型(JSON 数组)
* @return true 表示为数组
*/
bool isArray() const;
/**
* @brief 判断当前 Value 是否为对象类型(JSON 对象)
* @return true 表示为对象
*/
bool isObject() const;
// ----- 数组/对象通用 API -----
/**
* @brief 返回数组或对象中元素的个数
* @note 若 Value 不是数组也不是对象,则返回 0
* @return 元素个数(数组长度或对象成员数量)
*/
ArrayIndex size() const;
/**
* @brief 判断 Value 为空,也能判断 Value 对象中的元素(数组或对象)是否为空(size() == 0)
* @return true 表示为空
*/
bool empty() const;
/**
* @brief 清空 Value 的内容,将其重置为 null
*/
void clear();
/**
* @brief 调整数组的大小
* @param newSize 新的数组长度
* @note 如果当前 Value 不是数组,则先转换为空数组;若新长度大于原长度,新增元素为 null
*/
void resize(ArrayIndex newSize);
// ----- 数组专用 API -----
/**
* @brief 通过下标访问数组元素(返回引用,可修改)
* @param index 数组下标(从 0 开始)
* @return 对应元素的引用,若下标超出当前数组范围且 Value 是数组,则会自动扩展
* @note 若当前 Value 不是数组,则先转换为数组
*/
Value& operator[](ArrayIndex index);
/**
* @brief 向数组末尾追加一个元素
* @param value 要追加的值
* @return 新添加元素的引用
* @note 若当前 Value 不是数组,则先转换为数组
*/
Value& append(const Value& value);
// ----- 对象专用 API -----
/**
* @brief 通过 C 字符串键访问对象成员(返回引用,可修改)
* @param key 成员名(C 字符串)
* @return 对应成员的引用,若成员不存在则自动创建为 null
* @note 若当前 Value 不是对象,则先转换为对象
*/
Value& operator[](const char* key);
/**
* @brief 通过 std::string 键访问对象成员(返回引用,可修改)
* @param key 成员名
* @return 对应成员的引用,若成员不存在则自动创建为 null
* @note 若当前 Value 不是对象,则先转换为对象
*/
Value& operator[](const String& key);
/**
* @brief 移除对象中指定键的成员
* @param key 要移除的成员名
* @note 若键不存在,则无效果
*/
void removeMember(const String& key);
/**
* @brief 判断对象中是否存在指定的键
* @param key 要查询的成员名
* @return true 表示存在该成员
*/
bool isMember(const String& key) const;
/**
* @brief 将 Value 格式化为带缩进的 JSON 字符串(便于阅读)
* @return 格式化后的字符串
* @note 该函数主要用于调试或输出,性能较低;生产环境建议使用 StreamWriter
*/
String toStyledString() const;
};
}
2.6 Json::Value 示例
#include <jsoncpp/json/json.h>
#include <iostream>
int main() {
Json::Value root;
// 添加元素
root["name"] = "张三";
root["age"] = 25;
root["address"]["city"] = "北京"; // 自动创建 address 对象
root["score"].append(10); // 自动创建 score 数组
root["score"].append(20.5);
std::cout << root.toStyledString() << std::endl;
// 访问元素
std::cout << root["name"].asString() << std::endl;
std::cout << root["age"].asInt() << std::endl;
std::cout << root["address"]["city"].asString() << std::endl;
// 访问数组元素
size_t n = root["score"].size();
for(int i = 0; i < n; i++)
std::cout << root["score"][i] << std::endl;
return 0;
}
// 输出结果
{
"address" :
{
"city" : "\u5317\u4eac"
},
"age" : 25,
"name" : "\u5f20\u4e09",
"score" :
[
10,
20.5
]
}
张三
25
北京
10
20.5
2.7 序列化
StreamWriter 是 jsoncpp 中负责序列化(将 Json::Value 写入流)的抽象基类。它定义了将 JSON 数据写入输出流的统一接口,具体的格式化行为由其子类实现。通常不直接实例化,而是通过 StreamWriterBuilder 工厂创建。
StreamWriterBuilder 是 jsoncpp 库中用于序列化(将 Json::Value 转换为字符串)的工厂类,它负责创建 StreamWriter 对象,并允许你精细地控制输出的 JSON 格式。
原型:
namespace Json {
class JSON_API StreamWriter {
public:
virtual ~StreamWriter();
// 核心方法:将 Value 写入输出流
virtual int write(Value const& root, std::ostream* sout) = 0;
};
}
namespace Json {
class JSON_API StreamWriterBuilder : public StreamWriter::Factory {
public:
// 构造函数
StreamWriterBuilder();
virtual ~StreamWriterBuilder();
// 核心工厂方法
virtual StreamWriter* newStreamWriter() const;
// 配置访问接口
Value& operator[](const String& key);
const Value& operator[](const String& key) const;
// 成员变量
Value settings_;
};
}
⚠️ write返回值:成功返回0,失败返回非0。
常用选项配置
Json::StreamWriterBuilder builder;
builder["indentation"] = ""; // 表示紧凑输出
builder["emitUTF8"] = true; // true: 直接输出中文等非 ASCII 字符(如 "张三");false: 将非 ASCII 字符转义为 \uXXXX 格式(如 "\u5F20\u4E09")
builder["dropNullPlaceholders"] = true; // 是否丢弃 null 值(默认 false)
builder["precision"] = 2; // 表示输出浮点数时保留多少位有效数字或小数位数
builder["commentStyle"] = "None"; // 默认None不输出注释,All输出所有注释
2.8 反序列化
CharReader 是反序列化的抽象基类,定义了将 JSON 字符串解析为 Json::Value 的接口。通常不直接实例化,而是通过 CharReaderBuilder 工厂创建。
CharReaderBuilder 是反序列化的工厂类,负责创建 CharReader 对象,并允许配置解析行为。
原型:
namespace Json {
class JSON_API CharReader {
public:
virtual ~CharReader();
// 核心方法:解析 JSON 字符串
virtual bool parse(char const* begin, char const* end,
Value* root, String* errs) = 0;
};
}
namespace Json {
class JSON_API CharReaderBuilder : public CharReader::Factory {
public:
CharReaderBuilder();
virtual ~CharReaderBuilder();
// 工厂方法:创建 CharReader 对象
virtual CharReader* newCharReader() const;
// 配置访问接口
Value& operator[](const String& key);
const Value& operator[](const String& key) const;
// 配置存储
Value settings_;
};
}
⚠️ parse返回值:成功返回 true,失败返回 false。
2.9 基本使用
#include <jsoncpp/json/json.h>
#include <iostream>
#include <sstream>
int main() {
Json::Value root;
// 添加元素
root["name"] = "张三";
root["age"] = 25;
root["address"]["city"] = "北京"; // 自动创建 address 对象
root["score"].append(10); // 自动创建 score 数组
root["score"].append(20.5);
// 序列化
Json::StreamWriterBuilder writeBuilder;
std::unique_ptr<Json::StreamWriter> writer(writeBuilder.newStreamWriter());
std::stringstream ss;
writer->write(root, &ss);
std::cout << "序列化:" << std::endl;
std::cout << ss.str() << std::endl;
// 反序列化
std::string json_str = ss.str();
Json::CharReaderBuilder readBuilder;
std::unique_ptr<Json::CharReader> reader(readBuilder.newCharReader());
std::string err;
Json::Value res;
reader->parse(json_str.c_str() , json_str.c_str() + json_str.size() , &res , &err);
std::cout << "反序列化:" << std::endl;
std::cout << res["name"].asString() << std::endl;
std::cout << res["age"].asInt() << std::endl;
if(!res["address"].isNull() && res["address"].isObject())
std::cout << res["address"]["city"].asString() << std::endl;
if(!res["score"].isNull() && res["score"].isArray()){
size_t n = res["score"].size();
for(int i = 0; i < n; i++)
std::cout << res["score"][i] << std::endl;
}
return 0;
}
// 输出结果
序列化:
{
"address" :
{
"city" : "\u5317\u4eac"
},
"age" : 25,
"name" : "\u5f20\u4e09",
"score" :
[
10,
20.5
]
}
反序列化:
张三
25
北京
10
20.5
三、Jsoncpp 二次封装
util.h:
#pragma once
#include <jsoncpp/json/json.h>
#include <iostream>
#include <sstream>
#include <memory>
#include <optional>
namespace util {
// JSON 而不是 Json 防止和 jsoncpp 中的重名
struct JSON {
// 注意,函数实现时,无需加 static 和 缺省参数
static std::optional<std::string> serialize(const Json::Value& value , bool style = false);
static std::optional<Json::Value> unserialize(const std::string& input);
};
}
-
类成员函数定义时需要 static,实现时无需 static 。
-
类成员函数定义时的缺省参数,实现时不需要缺省参数。
-
定义时需要类域声明。
util.cc:
#include "util.h"
namespace util {
// 定义不需要 static 关键字
std::optional<std::string> JSON::serialize(const Json::Value& value , bool style) {
Json::StreamWriterBuilder writeBuilder;
if(style) {
writeBuilder["emitUTF8"] = true;
writeBuilder["indentation"] = " ";
} else {
writeBuilder["emitUTF8"] = false;
writeBuilder["indentation"] = "";
}
std::unique_ptr<Json::StreamWriter> writer(writeBuilder.newStreamWriter());
std::stringstream ss;
if(writer->write(value, &ss)) {
// 序列化失败
std::cout << "序列化失败!" << std::endl;
return std::nullopt;
}
return ss.str();
}
std::optional<Json::Value> JSON::unserialize(const std::string& input) {
Json::CharReaderBuilder readBuilder;
std::unique_ptr<Json::CharReader> reader(readBuilder.newCharReader());
std::string err;
Json::Value res;
if(!reader->parse(input.c_str() , input.c_str() + input.size() , &res , &err)) {
// 反序列化失败
std::cout << input << " 序列化失败: " << err << std::endl;
return std::nullopt;
}
return res;
}
}
test.cc:
#include "util.h"
int main() {
// 序列化测试
Json::Value root;
root["name"] = "张三";
root["age"] = 25;
root["address"]["city"] = "北京";
root["score"].append(10);
root["score"].append(20.5);
std::optional<std::string> json_str = util::JSON::serialize(root);
if(json_str) {
std::cout << "序列化结果\n" << json_str.value() << std::endl;
}
// 反序列化测试
std::string str = R"({
"address":{
"city":"Anytown",
"state":"CA",
"street":"123 Main St"
},
"age":30,
"is_student":false,
"name":"John Doe"
})";
std::optional<Json::Value> val = util::JSON::unserialize(str);
if(val) {
std::cout << "反序列化结果\n";
std::cout << val.value()["address"]["city"].asString() << std::endl;
std::cout << val.value()["address"]["state"].asString() << std::endl;
std::cout << val.value()["address"]["street"].asString() << std::endl;
std::cout << val.value()["age"].asInt() << std::endl;
std::cout << val.value()["name"].asString() << std::endl;
std::cout << val.value()["is_student"].asBool() << std::endl;
}
return 0;
}
输出结果:
序列化结果
{"address":{"city":"\u5317\u4eac"},"age":25,"name":"\u5f20\u4e09","score":[10,20.5]}
反序列化结果
Anytown
CA
123 Main St
30
John Doe
0
四、补充概念
4.1 R"()"
R"()" 原始字符串字面量(Raw String Literal),是 C++11 引入的原始字符串字面量语法,用于表示字符串而不需要转义特殊字符。
使用场景:
-
JSON字符串 -
正则表达式
-
避免转移字符
-
多行字符串
避免转移字符
// 传统方式:需要转义引号和反斜杠
std::string json1 = "{\"name\":\"张三\",\"age\":25}";
// 原始字符串:直接写,无需转义
std::string json2 = R"({"name":"张三","age":25})";
// 传统方式:需要转义反斜杠
std::string path1 = "C:\\Users\\name\\file.txt";
// 原始字符串:直接写
std::string path2 = R"(C:\Users\name\file.txt)";
多行字符串无需续行符
std::string str = R"({
"address":{
"city":"Anytown",
"state":"CA",
"street":"123 Main St"
},
"age":30,
"is_student":false,
"name":"John Doe"
})";
4.2 std::optional
std::optional 是 C++17 引入的一个标准库特性,定义在 <optional> 头文件中。
它本质上是一个容器适配器,用来表示一个对象可能存在,也可能不存在的状态。你可以把它理解为一个安全的包装器,它要么包含一个有效值,要么处于空 std::nullopt 状态。
核心用途
在 std::optional 出现之前,当函数需要表达可能无返回值时,通常有以下几种处理方式,但各有缺陷:
-
返回特殊值(如
-1、nullptr):依赖于调用方记住并检查该特殊值,且特殊值可能本身就是合法值。 -
通过输出参数 +
bool返回值:代码不够直观等问题。
std::optional 优雅地解决了这些问题,将值的存在性作为类型的一部分显式表达出来。
-
构造:可以直接用
T类型的值构造,或者用std::nullopt构造为空。 -
has_value()或operator bool检查是否有值。 -
value()或operator* / operator->访问内部值(前提确保optional非空)。 -
value_or(默认值)没有返回值,则使用默认值。 -
reset()清空 optional。
示例:
#include <iostream>
#include <optional>
#include <string>
// 函数:根据 ID 查找名字,可能找不到
std::optional<std::string> findName(int id) {
if (id == 1) {
return "Alice"; // 包含值
}
if (id == 2) {
return "Bob";
}
return std::nullopt; // 明确表示无值
}
int main() {
// 1. 检查是否有值
std::optional<std::string> result = findName(1);
if (result.has_value()) {
std::cout << "Found: " << *result << "\n"; // 使用 * 或 .value() 访问
}
// 2. 使用 value_or 提供默认值
std::cout << findName(99).value_or("Unknown") << "\n"; // 输出 Unknown
// 3. 直接用于条件判断 (支持 bool 转换)
std::optional<std::string> name = findName(2);
if (name) {
std::cout << "Found: " << *name << "\n";
}
return 0;
}
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)