C++开源库使用:nlohmann/json
1. 简介
这个库应该是最火的一个json
解析的c++的开源库了吧!
可它是个模板库,我基本看不懂它啊!
不过学会怎么用就够了吧,
我用它主要目的是给我的avl
写测试样例时,
可以直接从json
文件进行读入测试样例。
我也似乎不是第一次用这个库了,之前也用过不过没写博客记录。
2. 构建
这个库提供了一个header-only
的版本,
可以直接把single_include
这个文件夹给放到头文件夹下。
不过我这里用的是cmake
的fetchcontent
# 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
文件,下面的代码就够了!
#include <fstream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;// ...std::ifstream f("example.json");
json data = json::parse(f);
你如果想直接读取内容,直接访问就好了。
如果想要转化,可以在后面跟get<T>()
std::vector<int> arr{ data["array"].get<std::vector<int>>()};
我主要是要用一下序列化反序列化这个功能,
就是把一个类或者对象给转化成一个json
对象,最终写入到文件或者是读到内存中来。
一般的对象其实都不用你做这一步,主要是你自定义的类需要搞。
这个库提供了一个宏来帮助你来完成这个事情
比如下面
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
这两个方法。
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
数据之后还需要再写回到文件中去。
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个了。
所以有时候有数组的对象会占据非常多的行,暂时还没有解决这个
问题,不过能跑就行。。。哈哈哈。
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