《C++ 工具库实战》C++ 解析利器 JsonCpp:从基础使用到封装简易 JSON 工具

🔥小叶-duck:个人主页

❄️个人专栏:《Data-Structure-Learning》《C++入门到进阶&自我学习过程记录》

《Linux系统从入门到实践》《Linux网络从入门到实践》

《Qt 方寸极境》 《MySQL》

✨未择之路,不须回头
已择之路,纵是荆棘遍野,亦作花海遨游


目录

前言

[一、Json 简介](#一、Json 简介)

[1.1 基本结构](#1.1 基本结构)

[1.2 语法规则](#1.2 语法规则)

[1.3 演示示例](#1.3 演示示例)

[1.4 用途](#1.4 用途)

[二、JsonCpp 介绍](#二、JsonCpp 介绍)

[2.1 JsonCpp 安装](#2.1 JsonCpp 安装)

​编辑

[三、JsonCpp 使用](#三、JsonCpp 使用)

[3.1 包含头文件](#3.1 包含头文件)

[3.2 链接库](#3.2 链接库)

[3.3 Json::Value 类型枚举](#3.3 Json::Value 类型枚举)

[3.4 中间数据转储类 Json::Value 常用 API](#3.4 中间数据转储类 Json::Value 常用 API)

[3.4.1 代码案例:Json::Value 构造 JSON](#3.4.1 代码案例:Json::Value 构造 JSON)

[3.5 序列化](#3.5 序列化)

[3.5.1 核心类介绍](#3.5.1 核心类介绍)

[3.5.2 序列化代码示例](#3.5.2 序列化代码示例)

[3.6 反序列化](#3.6 反序列化)

[3.6.1 核心类介绍](#3.6.1 核心类介绍)

[3.6.2 反序列化代码示例](#3.6.2 反序列化代码示例)

[3.6.3 序列化 & 反序列化对比小结](#3.6.3 序列化 & 反序列化对比小结)

[四、Jsoncpp 二次封装](#四、Jsoncpp 二次封装)

[4.1 封装目的](#4.1 封装目的)

[4.2 接口设计思路](#4.2 接口设计思路)

[4.3 头文件 Myjson.h](#4.3 头文件 Myjson.h)

[4.4 源文件 Myjson.cc 实现](#4.4 源文件 Myjson.cc 实现)

[4.5 测试代码 Myjson_test.cc](#4.5 测试代码 Myjson_test.cc)

[4.6 Makefile](#4.6 Makefile)

[4.7 编译与运行](#4.7 编译与运行)

[4.8 封装优势小结](#4.8 封装优势小结)

结束语


前言

在 C++ 后端网络项目开发中,配置文件读取、前后端报文交互、日志结构化输出,都离不开 JSON 这种轻量文本格式。原生 C++ 语言本身并没有内置 JSON 解析能力,我们需要借助第三方库完成 JSON 的构造、序列化与反序列化工作,JsonCpp 便是 C++ 生态里经典、易用的老牌 JSON 开源库。

本篇文章将从 JSON 基础语法入手,先介绍 JsonCpp 的安装方式、核心数据结构Json::Value以及常用 API,手把手演示 JSON 对象构建、序列化输出、字符串反解析等基础操作。 在掌握原生 JsonCpp 使用之后,我们会发现原生接口在工程使用上存在一定冗余,错误处理不够统一。因此我们会基于 JsonCpp 做一层二次封装,自研MyJson工具层,对外提供简洁的序列化、反序列化接口,统一异常与日志处理,屏蔽底层 JsonCpp 的复杂细节。

一、Json 简介

JSON (JavaScript Object Notation)是一种轻量级的数据交换格式 ,易于人工阅读编写,同时便于机器解析与生成。它源自 JavaScript 的子集,但JSON 本身语言无关,几乎所有编程语言都提供了对应的解析工具。

1.1 基本结构

JSON 只有两种核心容器结构,所有复杂数据都由二者组合嵌套而成:

  • 对象: 一组无序键值对集合,使用**大括号 {}**包裹;
  • 数组: 一组有序值的集合,使用**方括号 \[\]**包裹;
  • **值的类型:**支持字符串、数字、布尔值 true/false、null,也可以嵌套对象、数组。

1.2 语法规则

JSON 有严格的语法规范,书写时必须遵守,否则解析会直接失败:

  • 键值对 格式为 "key": value ,键与值之间用 **冒号 :**分隔;
  • 同一层级多个键值对 / 数组元素之间,使用 **逗号, **分隔;
  • 所有键必须为字符串类型,且必须使用双引号"包裹,单引号不符合 JSON 标准。

1.3 演示示例

bash 复制代码
{
    "name": "John Doe",
    "age": 30,
    "is_student": false,
    "skills": ["JavaScript", "Python", "C++"],
    "address": {
        "street": "123 Main St",
        "city": "Anytown",
        "state": "CA"
    }
}

1.4 用途

  • **数据存储:**作为一种数据格式,用于文件和数据库存储。
  • **数据交换:**作为 APIs 和网络请求中数据交换的标准格式。
  • **配置文件:**用于应用程序的配置信息。

二、JsonCpp 介绍

JsonCpp 是一个高性能的C++ JSON 库 ,专门用来处理 JSON 数据 。

它完整支持 JSON 标准,具备解析 JSON 字符串、生成 JSON 字符串、读写 JSON 对象与数组等能力。

2.1 JsonCpp 安装

Ubuntu 直接命令安装:

bash 复制代码
sudo apt install libjsoncpp-dev

CentOS 直接命令安装:

CentOS 系统可以通过 yum 包管理器直接安装开发库:

bash 复制代码
# CentOS7 / CentOS8
sudo yum install jsoncpp-devel

说明: CentOS 的包名后缀是-devel,和 Ubuntu 的-dev命名习惯不一样。

验证是否安装成功:

安装完成后,可通过下面命令查看头文件与库文件,验证是否安装成功:

bash 复制代码
# 查看头文件
ls /usr/include/jsoncpp/

# 查看库文件
ls /usr/lib/x86_64-linux-gnu/libjsoncpp*

安装完成结果:

三、JsonCpp 使用

3.1 包含头文件

使用该类需要引入头文件:

cpp 复制代码
#include <jsoncpp/json/json.h>

3.2 链接库

编译时,需要链接 jsoncpp 库,编译选项添加:

bash 复制代码
-ljsoncpp

3.3 Json::Value 类型枚举

Json::ValueType 枚举定义了 JSON 支持的全部数据类型:

cpp 复制代码
namespace Json {
  enum ValueType {
    nullValue = 0,     ///< 'null' 值
    intValue,          ///< 有符号整型
    uintValue,         ///< 无符号整型
    realValue,         ///< 浮点double值
    stringValue,       ///< UTF-8字符串
    booleanValue,      ///< 布尔值
    arrayValue,        ///< 数组(有序列表)
    objectValue        ///< 对象,键值对集合
  };

3.4 中间数据转储类 Json::Value 常用 API

JsonCpp 中最核心的类是 Json::Value,作为内存中间数据转储类。

  • 序列化场景: 将 C++ 中的各类数据,先存入Json::Value对象,再转为 JSON 字符串。
  • 反序列化场景: 解析 JSON 字符串,解析后的结果全部存放到Json::Value对象,之后从中读取字段。
cpp 复制代码
class JSON_API Value {
public:
    // 拷贝交换
    void swap(Value& other);
    void copy(const Value& other);

    // 类型转换接口:从Value取出对应类型数据
    const char* asCString() const;
    String asString() const;
    Int asInt() const;
    UInt asUInt() const;
    Int64 asInt64() const;
    UInt64 asUInt64() const;
    float asFloat() const;
    double asDouble() const;
    bool asBool() const;

    // 类型判断接口,判断当前Value是什么类型
    bool isNull() const;
    bool isBool() const;
    bool isInt() const;
    bool isInt64() const;
    bool isUInt() const;
    bool isUInt64() const;
    bool isIntegral() const;
    bool isDouble() const;
    bool isNumeric() const;
    bool isString() const;
    bool isArray() const;
    bool isObject() const;

    // 数组相关API
    ArrayIndex size() const;
    bool empty() const;
    void clear();
    void resize(ArrayIndex newSize);
    Value& operator[](ArrayIndex index);  // 通过下标访问数组元素
    Value& append(const Value& value);    // 向数组追加元素

    // 对象键值访问API
    Value& operator[](const char* key);
    Value& operator[](const String& key);
    void removeMember(const String& key); // 删除指定key
    bool isMember(const String& key) const; // 判断key是否存在

    // 序列化:将Value对象转为格式化JSON字符串
    String toStyledString() const;
};

核心接口说明:

  1. **\[\] 运算符:**用于对象读写键值对。val"name" = "zhangsan",直接给对象的 key 赋值。
  2. **append():**数组专用接口,往数组里面追加元素。
  3. **asXXX()系列:**取值接口。把Json::Value里的数据转成 C++ 基础类型,例如 .asString()、.asDouble()。
  4. **isXXX()系列:**类型判断。判断当前节点是 null / 对象 / 数组 / 数字等,防止取值类型不匹配。
  5. **toStyledString():**序列化函数。把内存中的Json::Value转为带缩进格式化的 JSON 字符串。

3.4.1 代码案例:Json::Value 构造 JSON

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

int main()
{
    // 1. 创建Json::Value对象,默认是object类型,用来存储键值对
    Json::Value val;
    val["name"] = "zhangsan";    // []运算符,给对象添加key-value,字符串
    val["age"] = 18;             // 写入整型

    // 给score数组追加浮点元素,val["score"]自动识别为数组
    val["score"].append(76.5);
    val["score"].append(86.5);
    val["score"].append(96.5);

    // 2. 手动创建一个数组对象
    Json::Value arr(Json::arrayValue);

    // 创建对象item1,存入id和name,追加到数组arr中
    Json::Value item1;
    item1["id"] = 1;
    item1["name"] = "Tom";
    arr.append(item1);

    // 创建对象item2,存入id和name,追加到数组arr中
    Json::Value item2;
    item2["id"] = 2;
    item2["name"] = "Jerry";
    arr.append(item2);

    // 将数组arr作为users字段,存入顶层val对象
    val["users"] = arr;

    // 3. 序列化:Value对象转格式化JSON字符串
    std::string json_str = val.toStyledString();
    // 打印输出json字符串
    std::cout << json_str << std::endl;

    return 0;
}

编译命令参考:

bash 复制代码
g++ json_test1.cc -o json_test1 -ljsoncpp

运行输出 JSON:

3.5 序列化

序列化的目标: 将内存中 Json::Value 对象,转换为 JSON 格式字符串。

JsonCpp 提供 StreamWriter 作为序列化核心类,采用建造者模式来创建写入器对象,支持自定义序列化格式。

3.5.1 核心类介绍

cpp 复制代码
namespace Json {
class JSON_API StreamWriter {
public:
    // 核心写接口:把root的json数据输出到输出流sout
    virtual int write(Value const& root, OStream* sout);

    // 建造者工厂基类
    class JSON_API Factory {
    public:
        virtual StreamWriter* newStreamWriter();
    };
};

// 全局辅助函数,根据工厂配置生成json字符串
String JSON_API writeString(
    StreamWriter::Factory const& factory,
    Value const& root);

// StreamWriter建造者实现类,用来配置序列化参数
class streamWriterBuilder : public StreamWriter::Factory {
public:
    Json::Value settings_;
};
}
  • StreamWriter:序列化核心类。write方法完成写入,接收Json::Value对象与输出流,返回值代表执行状态,一般情况下序列化不会失败。
  • StreamWriter::Factory:工厂基类。定义创建StreamWriter的接口。
  • StreamWriterBuilder:建造者派生类。继承工厂,用于配置序列化参数(缩进、换行等),调用 newStreamWriter() 创建 StreamWriter 实例。

设计思路: 建造者模式,把对象构造 和对象使用分开。我们先通过 Builder 配置输出风格,再生产对应的 StreamWriter,实现灵活自定义 JSON 输出格式。

3.5.2 序列化代码示例

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

int main()
{
    // 1. 创建Json::Value对象,默认是object类型,用来存储键值对
    Json::Value val;
    val["name"] = "zhangsan"; // []运算符,给对象添加key-value,字符串
    val["age"] = 18;         // 写入整型

    // 给score数组追加浮点元素,val["score"]自动识别为数组
    val["score"].append(76.5);
    val["score"].append(86.5);
    val["score"].append(96.5);

    // 2. 创建建造者对象,用于配置序列化输出参数
    Json::StreamWriterBuilder builder;
    builder["indentation"] = ""; // 配置缩进为空字符串,输出紧凑无换行的json

    // 3. 通过建造者newStreamWriter()生成StreamWriter序列化对象
    // 使用unique_ptr自动管理内存,避免内存泄漏
    std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());

    // 4. 字符串流,用来接收序列化之后的json字符串
    std::stringstream ss;
    // 调用write接口,将val写入字符串流ss
    int res = writer->write(val, &ss);
    if (res != 0)
    {
        std::cout << "write false" << std::endl;
        return -1;
    }

    // 5. 从stringstream中取出完整json字符串
    std::string json_str = ss.str();
    std::cout << json_str << std::endl;

    return 0;
}

代码分析:

  1. **数据组装部分:**构建Json::Value对象,写入普通键值对与数组,和前面代码基础用法一致。
  2. StreamWriterBuilder builder: 实例化建造者,builder"indentation" = "" 修改缩进配置。
    如果设置" ",输出就是带缩进格式化 JSON;空字符串代表紧凑模式,去掉多余换行空格。
  3. **builder.newStreamWriter():**工厂方法,动态创建StreamWriter实例,交给 unique_ptr 托管,自动释放内存。
  4. **writer->write(val, &ss):**序列化核心调用,将 Json::Value 里面的所有数据,写入 C++ 字符串流 stringstream。返回 0 代表成功,非 0 表示异常。
  5. **ss.str():**从流中提取最终的 JSON 字符串,后续可以打印、写入文件或者网络发送。

编译命令:

bash 复制代码
g++ json_test2.cc -o json_test2 -ljsoncpp

运行结果展示:

3.6 反序列化

反序列化作用:把 JSON 格式的字符串,解析到内存的 Json::Value 对象中,之后我们就可以读取里面存储的数据。

和序列化一样,JsonCpp 这里同样使用建造者模式,通过 CharReader 完成解析,CharReaderBuilder用来生产 CharReader 实例。

3.6.1 核心类介绍

cpp 复制代码
namespace Json {
class JSON_API CharReader {
public:
    // 核心解析接口,将一段json文本解析存入root
    virtual bool parse(
        char const* beginDoc,    // json字符串起始地址
        char const* endDoc,      // json字符串末尾地址
        Value* root,             // 输出:解析结果存入这个Value对象
        String* errs) = 0;       // 输出:解析失败时,存放错误信息,默认为nullptr

    // 工厂基类,定义创建CharReader的接口
    class JSON_API Factory {
        virtual CharReader* newCharReader() const = 0;
    };
};

// CharReader建造者,继承工厂,可配置解析参数
class JSON_API CharReaderBuilder : public CharReader::Factory {
public:
    Json::Value settings_;
    CharReader* newCharReader() const override;
};
}
  • **CharReader:反序列化核心类。**parse是解析函数。传入字符串首尾指针,解析成功返回 true;失败返回 false,错误信息存入 errs。
  • CharReader::Factory:工厂抽象基类。规定创建 CharReader 的接口。
  • CharReaderBuilder:建造者实现类,继承 Factory。调用newCharReader()生成 CharReader 对象,也可以通过 settings_配置解析规则。

**整体流程:实例化建造者 -> 通过建造者生成 CharReader 对象 -> 调用 parse 完成反序列化。**和前面 StreamWriter 序列化的设计完全对应。

3.6.2 反序列化代码示例

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

int main()
{
    // 错误写法;在字符串中不能直接写双引号,否则会识别出现问题(编译器会识别"{"是字符串,后续代码是乱的)
    //  std::string json_str = "{"age":18,"name":"zhangsan","score":[76.5,86.5,96.5]}";

    // 如果需要在字符串中使用双引号,就需要大量转义\",很麻烦
    //  std::string json_str = "{\"age\":18,\"name\":\"zhangsan\",\"score\":[76.5,86.5,96.5]}";

    // C++11原始字符串字面量R"()",不需要手动转义双引号,简化json字符串书写
    std::string json_str = R"({"age":18,"name":"zhangsan","score":[76.5,86.5,96.5]})";

    // 1.实例化建造者对象
    Json::CharReaderBuilder builder;
    // 2.调用newCharReader生产CharReader,unique_ptr自动管理内存
    std::unique_ptr<Json::CharReader> reader(builder.newCharReader());

    // 3.定义Value对象,用于存放解析之后的数据
    Json::Value val;
    std::string err_str; // 保存解析失败的错误信息

    // 4.核心parse解析接口
    // 参数1:字符串起始指针;参数2:字符串末尾指针;参数3:解析输出对象;参数4:错误信息
    bool res = reader->parse(json_str.c_str(), json_str.c_str() + json_str.size(), &val, &err_str);
    if (res == false)
    {
        std::cout << "parse json failed, err: " << err_str << std::endl;
        return -1;
    }

    // 5.解析成功,从Json::Value读取数据
    std::cout << "name: " << val["name"].asString() << std::endl;
    std::cout << "age: " << val["age"].asInt() << std::endl;

    // 读取数组,先判断节点类型,防止数组不存在或者不是数组,避免程序崩溃
    int size = val["score"].size();
    if (!(val["score"].isNull()) && val["score"].isArray())
    {
        for (int i = 0; i < size; i++)
        {
            std::cout << "score: " << val["score"][i].asDouble() << std::endl;
        }
    }

    return 0;
}

代码分析:

  • JSON 字符串定义 使用 C++ 原始字符串R"(...)",字符串内部的双引号不用转义。如果不用 R,JSON 里每个"都要写成\",代码可读性很差,也容易写错。
  • parse 解析函数:
    • **json_str.c_str():**字符串起始地址
    • **json_str.c_str() + json_str.size():**字符串末尾地址(左闭右开区间)
    • **&val:**解析完成后,json 数据存入这个 Json::Value 对象
    • **&err_str:**解析出错,会把错误描述字符串写入 err_str 返回 bool:true 代表解析成功,false 解析失败。
  • 读取基础字段 val"name".asString():取出 name 节点,转为 std::string;asInt() 取出 age 整型。必须调用 asXX (),不能直接输出 val "name"。
  • 数组安全读取先判断 val"score" 不为空,并且类型是数组,再遍历读取。工程开发中这个判断非常关键:如果 json 缺少 score 字段,直接遍历会触发异常崩溃。

编译命令:

bash 复制代码
g++ json_test3.cc -o json_test3 -ljsoncpp

运行结果展示:

3.6.3 序列化 & 反序列化对比小结

  • **序列化:**StreamWriterBuilder → StreamWriter → write(),内存 Value → JSON 字符串
  • 反序列化: CharReaderBuilder → CharReader → parse(),JSON 字符串 → 内存 Value
    两套接口都是建造者模式,API 设计对称。

四、Jsoncpp 二次封装

4.1 封装目的

原生 jsoncpp 库直接使用时,每次序列化 / 反序列化都需要重复编写建造器对象创建、unique_ptr 管理、错误判断等样板代码。 在项目中反复手写会造成大量重复代码,不利于统一的错误处理与日志打印。 因此我们封装一个 Json 工具类,对外暴露简洁接口,把底层 jsoncpp 的细节全部隐藏。

4.2 接口设计思路

  • 工具类名称:MyJson::JSON ,所有接口定义为静态成员函数,不需要实例化对象,直接类名调用
  • 序列化接口:
    • **输入:**Json::Value&(内存中的 json 对象)
    • **返回:**std::optional<std::string>,成功返回 json 字符串;失败返回std::nullopt
  • 反序列化接口:
    • **输入:**std::string&(json 格式字符串)
    • 返回:std::optional<Json::Value>,成功返回解析后的 Value 对象;失败返回std::nullopt

std::optional 是 C++17 特性,适合表示可能失败的返回值,可以清晰区分「调用成功带回结果」和「调用失败无结果」,比输出参数、返回错误码更现代。

4.3 头文件 Myjson.h

cpp 复制代码
#ifndef __MYJSON_H__
#define __MYJSON_H__

#include <iostream>
#include <jsoncpp/json/json.h>
#include <memory>
#include <sstream>
#include <optional>
#include "Mylog.h"

using namespace Mylog;

namespace MyJson
{
    class JSON
    {
    public:
        // 序列化:Json::Value对象 -> json字符串
        static std::optional<std::string> serialize(Json::Value &val);
        // 反序列化:json字符串 -> Json::Value对象
        static std::optional<Json::Value> unserialize(std::string &input);
    };
}

#endif

头文件代码分析:

  1. 引入 optional 头文件,用于接口返回值;同时引入日志模块Mylog.h,失败时打印日志
  2. 放在 MyJson 命名空间,避免和其他库命名冲突
  3. 两个静态函数,对外屏蔽 StreamWriter、CharReaderBuilder 这些底层建造者细节

4.4 源文件 Myjson.cc 实现

cpp 复制代码
#include "Myjson.h"

namespace MyJson
{
    std::optional<std::string> JSON::serialize(Json::Value &val)
    {
        // 1. 创建序列化建造者,生产StreamWriter
        Json::StreamWriterBuilder builder;
        std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());

        std::stringstream ss;
        // 2. 将Value对象写入字符串流
        int ret = writer->write(val, &ss);
        if (ret != 0)
        {
            // 序列化失败,使用自定义日志打印错误
            ERR("序列化失败!");
            return std::nullopt;
        }
        // 序列化成功,返回字符串
        return ss.str();
    }

    std::optional<Json::Value> JSON::unserialize(std::string &input)
    {
        // 1. 创建反序列化建造者,生产CharReader
        Json::CharReaderBuilder builder;
        std::unique_ptr<Json::CharReader> reader(builder.newCharReader());

        Json::Value val;
        std::string err_str;
        // 2. 调用parse进行解析
        bool ret = reader->parse(input.c_str(), input.c_str() + input.size(), &val, &err_str);
        if (ret == false)
        {
            // 反序列化失败,打印原始输入字符串与错误信息
            ERR("反序列化失败: {}, {}", input, err_str);
            return std::nullopt;
        }
        // 解析成功,返回Value对象
        return val;
    }
}

源码逐段分析:

serialize 序列化函数
  • StreamWriterBuilder:建造者,用来创建StreamWriter对象,unique_ptr自动管理内存,不用手动 delete
  • std::stringstream ss:内存流,接收序列化后的文本
  • writer->write(val, &ss):将 Json::Value 中的数据转为 json 文本写入流,返回 0 代表成功
  • 失败:打印日志,返回std::nullopt;成功取出 stringstream 里的字符串返回
unserialize 反序列化函数
  • CharReaderBuilder:反序列化建造者,创建 CharReader 解析器
  • reader->parse():核心解析函数,传入字符串首尾指针,解析结果存入 val,错误信息存入 err_str
  • 解析失败:日志打印原始字符串和错误原因,返回std::nullopt;成功返回解析好的Json::Value

4.5 测试代码 Myjson_test.cc

cpp 复制代码
#include "Myjson.h"

using namespace MyJson;

void Serialize_test()
{
    Json::Value val;
    val["name"] = "zhangsan";
    val["age"] = 18;

    val["score"].append(76.5);
    val["score"].append(86.5);
    val["score"].append(96.5);

    // 错误写法:
    // std::string json_str = serialize(val);
    // std::cout << json_str << std::endl;

    auto result = JSON::serialize(val);
    std::string json_str = result.value();
    std::cout << json_str << std::endl;
}

void UnSerialize_test()
{
    std::string json_str = R"({"age":18,"name":"zhangsan","score":[76.5,86.5,96.5]})";
    auto result = JSON::unserialize(json_str);
    Json::Value val = result.value();

    std::cout << "name: " << val["name"].asString() << std::endl;
    std::cout << "age: " << val["age"].asInt() << std::endl;

    int size = val["score"].size();
    if (!(val["score"].isNull()) && val["score"].isArray())
    {
        for (int i = 0; i < size; i++)
        {
            std::cout << "score: " << val["score"][i].asDouble() << std::endl;
        }
    }
}

int main()
{
    Serialize_test();
    UnSerialize_test();
    return 0;
}

测试代码说明:

  • Serialize_test:构造 Json::Value 对象,调用封装好的 JSON::serialize 一键序列化,拿到 json 字符串
  • UnSerialize_test:传入 json 字符串,调用 JSON::unserialize 一键反序列化,得到 Json::Value,读取内部字段
  • 使用方式极大简化,不再需要每次手写建造者、parse、write 等底层代码,业务代码只关心数据本身

4.6 Makefile

bash 复制代码
CFLAGS=-I../source

myjson_test:Myjson_test.cc ../source/Myjson.cc ../source/Mylog.cc
	g++ -o $@ $^ -std=c++17 $(CFLAGS) -lpthread -ljsoncpp -lfmt -lspdlog

.PHONY:clean
clean:
	rm -rf myjson_test
  • **-std=c++17:**必须开启 C++17,std::optional是 C++17 引入的特性
  • **-ljsoncpp:**链接 jsoncpp 库;同时链接日志相关 spdlog、fmt 库
  • **-I../source:**指定头文件搜索路径,找到 Myjson.h、Mylog.h

4.7 编译与运行

4.8 封装优势小结

  • **简化业务代码:**业务层一行调用,不用重复写建造器、内存管理、错误处理
  • **统一错误处理:**所有 json 解析失败,统一走日志输出,方便项目问题排查
  • **底层隔离:**后续更换 json 库(例如 nlohmann),只需要修改 Myjson.cc 内部实现,业务代码完全不用改动
  • **内存安全:**内部使用 unique_ptr,自动释放解析器 / 写入器,避免内存泄漏

**注意点:**调用.value()获取 optional 里面的值,必须保证 optional 有值;工程中严谨写法要先判断 if(result.has_value()),再调用 value (),否则会抛异常。

结束语

到这里,本篇 JsonCpp 的实战内容就全部结束了。我们从 JSON 基础概念出发,学习了 JsonCpp 库的安装、核心 API 的调用,亲手完成 JSON 对象构造、序列化与反序列化等基础功能。

原生 JsonCpp 功能完备,但直接在业务代码中裸用会带来代码冗余、错误处理零散等问题。因此我们进行了二次封装,搭建MyJson工具层,将底层库的复杂接口做收拢,对外暴露简洁易用的序列化、反序列化能力,统一错误返回与日志处理。同时也探讨了工具类接口的设计思路,理解为什么序列化函数适合设计为静态成员函数,体会第三方库封装的工程思想。

本次封装的 MyJson 模块可以直接嵌入到 C++ 后端项目中,用于配置解析、网络报文处理。这个二次封装的思路也具备通用性,后续遇到其他第三方库时,都可以借鉴这套思路,屏蔽底层细节、统一异常处理,降低业务层的开发成本。

相关推荐
javachen__1 小时前
试试Jackson:把JSON拉平成普通属性
json
dialect_9 小时前
KMP算法
开发语言·c++·算法
ShineWinsu11 小时前
对于Qt:QRadioButton常见属性以及QButtonGroup的解析
数据库·c++·qt·microsoft·gui·qradiobutton·qbuttongroup
傲世仙尊11 小时前
多态-虚表才活到运行期符号表早已消失
c++
chenbingjie_c12 小时前
C++ 多态完全指南:从虚函数重写到虚函数表原理
开发语言·c++
是main不是漫12 小时前
【双指针——移动零】数组划分类题目的入门基石
c++·算法
·present·12 小时前
CSAPP(Bomb lab)
c语言·c++
王老师青少年编程12 小时前
2026年信奥赛c++普及组csp-j第二轮复赛真题
c++·真题·csp·信奥赛
时间的拾荒人13 小时前
CMake 常用命令详解:从版本声明到安装部署
c++·面试