数据织网者:Jsoncpp库深度解析——从C++原生JSON处理到工程级数据交互实战全攻略

文章目录

本篇摘要

本文详解C++生态经典库Jsoncpp的核心用法,涵盖环境搭建、新版API解析/生成功能(如CharReader/StreamWriterBuilder)、类型安全操作及文件交互,来帮助读者快速上手jsoncpp。


前言:

JSON(JavaScript Object Notation)是现代软件开发中最常用的轻量级数据交换格式之一,而 Jsoncpp 是 C++ 生态中最经典、最稳定的 JSON 处理库。无论你是开发配置文件解析、API 数据交互,还是游戏存档管理,掌握 Jsoncpp 都能让你高效处理 JSON 数据。


一、Jsoncpp 是什么?

Jsoncpp 是一个开源的 C++ 库,专门用于解析、生成和操作 JSON 数据。它的特点是:

  • 纯 C++ 实现 ,无需依赖第三方运行时环境。
  • API 设计简洁,符合 C++ 开发者的使用习惯。
  • 跨平台支持(Windows/Linux/macOS),兼容主流编译器(GCC/Clang/MSVC)。
  • 稳定性高,被广泛应用于游戏开发(如 Unreal Engine 插件)、嵌入式系统和后端服务。

二、环境准备:如何安装 Jsoncpp?

  1. 获取源码 :

    访问 Jsoncpp 的官方 GitHub 仓库(https://github.com/open-source-parsers/jsoncpp(点击即跳转)),下载最新版本的源码压缩包(如 jsoncpp-master.zip),解压到本地目录。

  2. 编译为静态库(以 Windows + Visual Studio 为例):

    • 打开解压后的文件夹,进入 jsoncpp-master\makefiles\vs71(根据你的 VS 版本选择,如 vs2022)。
    • 用 Visual Studio 打开 jsoncpp.sln 工程文件。
    • 在解决方案配置中选择 Release 模式,编译 lib_json 项目(生成静态库文件,如 jsoncpp.lib)。
    • 编译完成后,头文件在 include/json 目录,库文件在 libs 目录。
  3. 配置你的项目:

    • 将 include/json 目录添加到项目的 头文件搜索路径 (如 Visual Studio 中的 附加包含目录)。
    • 将编译生成的 jsoncpp.lib 添加到项目的 库文件搜索路径 (如 附加库目录 ),并在 链接器输入 中添加 jsoncpp.lib。

其他平台 :Linux/macOS 用户可通过包管理器直接安装(如 sudo apt-get install libjsoncpp-dev))。


三、Jsoncpp 的核心功能与代码示例

Jsoncpp 的核心是三个类:

  • Json::Value:表示 JSON 数据的通用容器(可以是对象、数组、字符串、数字等)。
  • Json::Reader (旧版)/Json::CharReader (新版):解析 JSON 字符串为 Json::Value 对象。
  • Json::StyledWriter (旧版)/Json::StreamWriterBuilder (新版):将 Json::Value 对象转换为格式化的 JSON 字符串。

注意: Jsoncpp 1.9.0 及以上版本推荐使用 CharReader 和 StreamWriterBuilder(更现代且线程安全),以下示例基于新版 API。


简单使用:

测试效果:

  • 这里可以看到转化成json串的形式就是一层套一层(通过花括号),如果是有数组也就是通过中括号规划出来,其次就是默认存储的时候以十六进制形式存储,因此传输起来也是比较方便的。

对应测试源码:

cpp 复制代码
#include <json/json.h>
#include <iostream>
#include <sstream>
#include <memory>

bool Serialize(Json::Value &jn, std::string &res_str)
{
  Json::StreamWriterBuilder swb;
  std::unique_ptr<Json::StreamWriter> psw(swb.newStreamWriter());

  std::stringstream ss;
  int ret = psw->write(jn, &ss);
  if (ret != 0)
  {
    std::cout << "Json反序列化失败!\n";
    return false;
  }
  res_str = ss.str();
  return true;
}

bool UnSerialize(std::string &res_str, Json::Value &jn)
{
  Json::CharReaderBuilder crb;
  std::shared_ptr<Json::CharReader> cr(crb.newCharReader());
  std::string errs;
  int ret = cr->parse(res_str.c_str(), res_str.c_str() + res_str.size(), &jn, &errs);
  if (ret == false)
  {
    std::cout << "json反序列化失败: " << errs << std::endl;
    return false;
  }
  return true;
}

int main()
{

  char name[] = "张三";
  int age = 18;
  float score[3] = {88, 89.5, 99};

  Json::Value stu;

  stu["姓名"] = name;
  stu["年龄"] = age;
  stu["成绩"].append(score[0]);
  stu["成绩"].append(score[1]);
  stu["成绩"].append(score[2]);
  std::string res;

  bool ret1 = Serialize(stu, res);
  if (ret1 == false)
    return -1;
  std::cout << res << std::endl;

  Json::Value uts;

  bool ret2 = UnSerialize(res, uts);
  if (ret2 == false)
    return -1;

  std::cout << uts["姓名"].asString() << std::endl;
  std::cout << uts["年龄"].asInt() << std::endl;
  int sz = uts["成绩"].size();
  for (int i = 0; i < sz; i++)
  {
    std::cout << uts["成绩"][i].asFloat() << std::endl;
  }
  return 0;
}

解释下:

  • 首先是构建对应json对象,也就是定义Json::Value 对象,然后这里是可以支持嵌套的,也就是可以把这个对象当成对象map来用即可,也就是key-value结构,对应的value可以是数组也就是必须通过append来添加对应成员,value也可以是json对象,类似循环嵌套接口,因此可以把对应描述事物的逻辑结构转化成这种嵌套逻辑模式,最后按照格式转化成json对象在最后变成json串即可。
  • StreamWriterBuilder,创建StreamWriter对象调用write接口来完成对应的把json对象写成对应json串(通过stringstream流)。
  • CharReaderBuilder,创建对应的CharReader来调用对应的parse接口把对应json串解析到对应传入的json对象里面。
  • 其次就是以json对象形式访问的对应的成员的时候,如果要拿到里面的值就是直接类似asString把它转化成对应类型进行后续操作即可。

四、常见问题与注意事项

  1. 旧版 vs 新版 API:

    • 旧版(如 Json::Reader 和 Json::StyledWriter)在 1.9.0 之前常用,但新版(Json::CharReader 和 Json::StreamWriterBuilder)更安全且线程友好,推荐优先使用。
    • 如果使用旧版,需包含 <json/reader.h> 和 <json/writer.h>,但新版已统一到 <json/json.h>。
  2. 类型安全:

    • 访问字段时务必用正确的 asXXX() 方法(如 asString()、asInt()),否则可能导致运行时错误。
    • 如果字段可能不存在,先用 root.isMember("字段名") 检查是否存在,或用 root.get("字段名", 默认值) 提供默认值(如 root.get("age", 0).asInt())。
  3. 文件读写:

    • 实际项目中常需要从文件加载 JSON 或保存 JSON 到文件。可以使用 std::ifstream 读取文件内容到字符串,再用上述解析方法;写入时则将生成的 JSON 字符串通过 std::ofstream 保存到文件。

    如(从文件读取 JSON):

    cpp 复制代码
    #include <fstream>
    std::ifstream file("config.json");
    std::string jsonStr((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
    // 然后用 Json::parseFromStream 解析 jsonStr

五、Jsoncpp核心接口总结

核心类/模块总结

类/模块 用途 头文件
Json::Value JSON 数据的万能容器(对象/数组/字符串/数字等) <json/json.h>
Json::CharReaderBuilder 解析 JSON 字符串 的配置器(新版推荐,线程安全) <json/json.h>
Json::CharReader 实际执行解析的底层类(通常通过 CharReaderBuilder 间接使用) <json/json.h>
Json::StreamWriterBuilder 生成 JSON 字符串 的配置器(新版推荐,线程安全) <json/json.h>
Json::StreamWriter 实际执行生成的底层类(通常通过 StreamWriterBuilder 间接使用) <json/json.h>

常用接口功能总结

功能分类 接口/类 所属模块 接口原型/关键方法 用途说明 典型使用场景 一行代码示例
1. 解析 JSON 字符串 Json::CharReaderBuilder 解析模块 Json::CharReaderBuilder builder; 创建 JSON 解析器的配置对象(线程安全) 解析 JSON 字符串前必须初始化 Json::CharReaderBuilder builder;
Json::parseFromStream 解析模块 bool Json::parseFromStream(<br> const Json::CharReaderBuilder& builder,<br> const char* begin, const char* end,<br> Json::Value* root,<br> std::string* errs<br>); 将 JSON 字符串(begin 到 end)解析为 Json::Value 对象,错误信息存到 errs 从文件/网络读取的 JSON 字符串转 C++ 对象 Json::parseFromStream(builder, jsonStr.data(), jsonStr.data()+jsonStr.size(), &root, &errs);
Json::CharReader 解析模块(底层) 通常不直接使用(通过 CharReaderBuilder 调用) 实际执行解析的底层类 底层扩展或特殊需求(新手一般不用) -
2. 生成 JSON 字符串 Json::StreamWriterBuilder 生成模块 Json::StreamWriterBuilder builder; 创建 JSON 生成器的配置对象(线程安全) 生成 JSON 字符串前必须初始化 Json::StreamWriterBuilder builder;
Json::writeString 生成模块 std::string Json::writeString(<br> const Json::StreamWriterBuilder& builder,<br> const Json::Value& root<br>); 将 Json::Value 对象转换为格式化的 JSON 字符串(可读性好) 将 C++ 数据结构保存为 JSON 文件/网络传输 std::string jsonStr = Json::writeString(builder, root);
Json::StreamWriter 生成模块(底层) 通常不直接使用(通过 StreamWriterBuilder 调用) 实际执行生成的底层类 底层扩展或特殊需求(新手一般不用) -
3. 操作 Json::Value(解析后的数据) rootconst std::string& key 通过键名访问 JSON 对象的字段(返回 Json::Value) 取对象中的字符串、数字等字段值 std::string name = root["name"].asString();
root["字段名"].asString() Json::Value std::string Json::Value::asString() const 将字段值转为 C++ 字符串 取 JSON 中的文本字段(如 "name": "Alice") std::string name = root["name"].asString();
root["字段名"].asInt() Json::Value int Json::Value::asInt() const 将字段值转为 C++ 整数 取 JSON 中的整数字段(如 "age": 25) int age = root["age"].asInt();
root["字段名"].asBool() Json::Value bool Json::Value::asBool() const 将字段值转为 C++ 布尔值 取 JSON 中的布尔字段(如 "isValid": true) bool valid = root["isValid"].asBool();
root["字段名"].asDouble() Json::Value double Json::Value::asDouble() const 将字段值转为 C++ 浮点数 取 JSON 中的浮点数字段(如 "price": 9.9) double price = root["price"].asDouble();
root.isMember("字段名") Json::Value bool Json::Value::isMember(const std::string& key) const 检查 JSON 对象是否包含某个字段 避免访问不存在的字段导致错误 if (root.isMember("name")) { ... }
root.isArray() Json::Value bool Json::Value::isArray() const 判断字段是否为 JSON 数组 安全遍历数组元素 if (root["skills"].isArray()) { ... }
root.isObject() Json::Value bool Json::Value::isObject() const 判断字段是否为 JSON 对象 安全访问嵌套对象 if (root["address"].isObject()) { ... }
rootsize_t index 通过索引访问 JSON 数组的元素 取数组中的某个值(如第 0 个元素) std::string skill = root["skills"][0].asString();
root["数组字段"].size() Json::Value size_t Json::Value::size() const 获取 JSON 数组的长度 遍历数组时获取元素数量 for (size_t i=0; i<root["skills"].size(); i++) { ... }
rootconst std::string& key 访问嵌套 JSON 对象的字段(多层对象) 取嵌套结构中的字段(如 "address.city") std::string city = root["address"]["city"].asString();
4. 配置生成/解析格式 builder["indentation"] = " " StreamWriterBuilder / CharReaderBuilder builder["indentation"] = " "(设置缩进字符串) 控制生成的 JSON 字符串格式(缩进美化) 让生成的 JSON 可读性高(调试用) builder["indentation"] = " ";
builder["indentation"] = "" StreamWriterBuilder / CharReaderBuilder builder["indentation"] = ""(空字符串无缩进) 控制生成的 JSON 字符串格式(紧凑无缩进) 生成紧凑 JSON(节省空间/网络传输) builder["indentation"] = "";

本篇小结

本篇详细接受jsoncpp如何安装,如何使用,以及简单例子介绍常见接口,还有就是最后常见的重要结构,来帮助读者快速上手jsoncpp书写。

相关推荐
(Charon)3 小时前
【C++面试】vector底层原理:扩容、拷贝移动与迭代器失效
c++·面试
朔北之忘 Clancy3 小时前
青少年软编等考七级题解目录
开发语言·c++·青少年编程·gesp·csp·信奥赛·noi
汉克老师4 小时前
GESP2026年9月认证C++七级( 第一部分选择题(8~15题)精讲
c++·gesp·小学生·学c++编程
Mr_Macallon4 小时前
C++回顾(基础)(01)
c语言·开发语言·c++·学习
如意猴4 小时前
【C++】007 C/C++ 内存管理机制、malloc与new的区别及模板初阶
java·c++·算法
朝朝辞暮i4 小时前
C++ 第 32 课:main()、rclcpp::init()、spin()、shutdown()
开发语言·c++·算法
汉克老师5 小时前
GESP2026年9月认证C++一级( 第三部分编程题(1、新龟兔赛跑))精讲
c++·gesp·小学生·学c++编程
沫璃染墨5 小时前
《从零入门Linux系统篇(五十七):线程篇·十——生产者消费者模型进阶:从环形缓冲区到POSIX信号量》
linux·运维·服务器·开发语言·c++·系统架构·信号处理
wuminyu5 小时前
LockStack在虚拟线程Mount和Unmount拷贝过程剖析
java·linux·c语言·jvm·c++
知识分享小能手6 小时前
C++ 学习教程,从入门到精通,string类和标准模板库 — 完整知识点详解(16)
开发语言·c++·学习