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;
}