C++开源库使用:nlohmann/json

1. 简介

这个库应该是最火的一个json解析的c++的开源库了吧!

可它是个模板库,我基本看不懂它啊!

不过学会怎么用就够了吧,

我用它主要目的是给我的avl写测试样例时,

可以直接从json文件进行读入测试样例。

我也似乎不是第一次用这个库了,之前也用过不过没写博客记录。

2. 构建

这个库提供了一个header-only的版本,

可以直接把single_include这个文件夹给放到头文件夹下。

不过我这里用的是cmake fetchcontent

sh 复制代码
# Typically you don't care so much for a third party library's tests to be
# run from your own project's code.
set(JSON_BuildTests OFF CACHE INTERNAL "")

# If you only include this third party in PRIVATE source files, you do not
# need to install it when your main project gets installed.
# set(JSON_Install OFF CACHE INTERNAL "")

# Don't use include(nlohmann_json/CMakeLists.txt) since that carries with it
# unintended consequences that will break the build.  It's generally
# discouraged (although not necessarily well documented as such) to use
# include(...) for pulling in other CMake projects anyways.
add_subdirectory(nlohmann_json)
...
add_library(foo ...)
...
target_link_libraries(foo PRIVATE nlohmann_json::nlohmann_json)

3. 使用

这个库使用起来非常简单

如果要解析一个json文件,下面的代码就够了!

cpp 复制代码
#include <fstream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;

// ...

std::ifstream f("example.json");
json data = json::parse(f);

你如果想直接读取内容,直接访问就好了。

如果想要转化,可以在后面跟get<T>()

cpp 复制代码
std::vector<int> arr{ data["array"].get<std::vector<int>>()};

我主要是要用一下序列化反序列化这个功能,

就是把一个类或者对象给转化成一个json对象,最终写入到文件或者是读到内存中来。

一般的对象其实都不用你做这一步,主要是你自定义的类需要搞。

这个库提供了一个宏来帮助你来完成这个事情

比如下面

cpp 复制代码
struct avl_test_case {

    avl_test_case() = default;

    avl_test_case(std::vector<int> &&v, 
                            std::vector< avl_cont_op > &&ops, 
                            std::vector<int> &&last_order_seqs):
                            input{std::move(v)},op{std::move(ops)},expect_seq{std::move(last_order_seqs)}
    {

    }

    avl_test_case(const  std::vector<int> &v, 
                 const std::vector<avl_cont_op> &ops,
                const  std::vector<int> &expect): input{v}, op{ops},expect_seq{expect}
    {

    }

    bool isEqual(const avl_test_case &tcase) {
        return tcase.expect_seq == expect_seq && tcase.input == input && tcase.op == op;
    }

    std::vector<int> input{};
    std::vector< avl_cont_op > op{};
    std::vector<int> expect_seq{};

  
};

NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(avl_test_case, input, op, expect_seq)

在生成json对象时,属性名就是对应的成员变量名。

当然你也可以自定来定义序列化和反序列化的这个过程。

就像下面那样,需要自己去实现to_json from_json这两个方法。

cpp 复制代码
enum avl_op_tp {
    AVL_INSERT = 0,
    AVL_DEL,
    AVL_FIND,
    AVL_NULL_OP
};

struct avl_cont_op {

    bool operator==(const avl_cont_op &op1) const{
        return tp == op1.tp && cnt == op1.cnt;
    }

    avl_op_tp tp{AVL_NULL_OP};
    uint32_t cnt{};
};

namespace nlohmann {
    template<>
    struct adl_serializer<avl_cont_op> {
        static void to_json(json& j, const avl_cont_op& cont_op) {
            j = json::object();
            j["tp"] = cont_op.tp;
            j["cnt"] = cont_op.cnt;
        }
        
        static void from_json(const json& j, avl_cont_op& cont_op) {
            j.at("tp").get_to(cont_op.tp);
            j.at("cnt").get_to(cont_op.cnt);
        }
    };
}

修改json文件的话,主要是拿到对应的json& 数据,注意一定是引用不然你的修改就不生效了。在修改完json数据之后还需要再写回到文件中去。

cpp 复制代码
void AVLTestManager::load_data() {
        std::ifstream file(filename_);
        if (!file.good()) {
            // 文件不存在,创建基本结构
            data_ = nlohmann::json::object();
            data_["hello"] = "world";
            data_["tests"] = nlohmann::json::array();
        } else {
            try {
                data_ = nlohmann::json::parse(file);
            } catch (const nlohmann::json::parse_error& e) {
                throw std::runtime_error("Failed to parse JSON file: " + std::string(e.what()));
            }
        }
    }
    
    // 保存数据到文件
void AVLTestManager::save_data() {
    std::ofstream file(filename_);
    if (!file.is_open()) {
        throw std::runtime_error("Failed to open file for writing: " + filename_);
    }
    file << std::setw(4) << data_ << std::endl;
}

再给一段代码吧,我也只是会用了。不太理解这个模样库。

还有个问题是写入json文件时,json文件的空白固定成4个了。

所以有时候有数组的对象会占据非常多的行,暂时还没有解决这个

问题,不过能跑就行。。。哈哈哈。

cpp 复制代码
class AVLTestManager{
public:

    explicit AVLTestManager(const std::string &filename):filename_{filename}
    {
        load_data();
    }

    bool addTestCase(const std::string &suite_name, const avl_test_case& test_case);
    bool removeTestSuite(const std::string &suite_name);
    bool removeTestCase(const std::string &suite_name, const avl_test_case& test_case);
    

    bool removeTestCaseByIndex(const std::string &suite_name, size_t idx);


    std::vector<avl_test_suite> getAllTestSuites();

private:
    void load_data();
    void save_data();


    nlohmann::json data_;
    std::string filename_;
};

bool AVLTestManager::addTestCase(const std::string& suiteName, const avl_test_case& testCase) {
    auto& testsArray = data_["tests"];
    bool suiteFound = false;
    bool caseFound = false;
    
    for (auto& suite : testsArray) {
        if (suite["suite_name"] == suiteName) {
            suiteFound = true;
            auto& casesArray = suite["cases"];
            
            // 检查是否已存在相同的测试用例
            for (const auto& existingCase : casesArray) {
                auto existingCaseObj = existingCase.get<avl_test_case>();
                if (existingCaseObj.isEqual(testCase)) {
                    caseFound = true;
                    break;
                }
            }
            
            if (!caseFound) {
                casesArray.push_back(testCase);
                save_data();
                return true;
            }
            break;
        }
    }
    
    if (!suiteFound) {
        // 创建新套件
        avl_test_suite newSuite;
        newSuite.suite_name = suiteName;
        newSuite.cases.push_back(testCase);
        
        testsArray.push_back(newSuite);
        save_data();
        return true;
    }
    
    return false;
}

4. 参考

nlohmann/json

相关推荐
Tisfy2 小时前
LeetCode 3508.设计路由器:STL套STL——有什么需求就设计什么数据结构
c++·leetcode·题解·设计·哈希表
夜猫逐梦2 小时前
【C++】Visual Studio+CMake 开发 C++ 入门指南:从环境搭建到项目实战
开发语言·c++·visual studio
tumu_C3 小时前
无用知识研究:用sfinae实现函数模板的overload [一]
开发语言·c++·算法
宛 禾3 小时前
list的学习
c++·学习
FFZero13 小时前
积加科技音视频一面
c++·科技·音视频
艾莉丝努力练剑4 小时前
【编码表 && STL】C++编程基石:从字符编码表到STL标准库的完整入门指南
java·linux·c++
工头阿乐4 小时前
Ubuntu 安装与使用C++ onnxruntime库
linux·c++·ubuntu
无挂写代码4 小时前
C++入门
开发语言·c++
努力努力再努力wz4 小时前
【C++进阶系列】:位图和布隆过滤器(附模拟实现的源码)
java·linux·运维·开发语言·数据结构·c++