使用c++版本nlohmann::json库快速写入、读取json文件
一、前言
nlohmann::json库本身提供了一些宏,能够实现快速实现序列化、反序列化的json目的。比如:
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(type, member...) // (1)
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(type, member...) // (2)
查看头文件可知(1)这个宏添加了两个友元函数负责实现序列化与反序列化。
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \
friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
使用此宏需满足两个条件:
1)Type必须有默认的构造函数,
2)宏必须要在类中(class/struct)使用。
并且需要注意的是:
1) 当前的实现被限制为最多64个成员变量。如果要序列化/反序列化具有超过64个成员变量的类型,则需要手动定义to_json/from_json函数。
2) 宏只适用于nlohmann::json类型;目前不支持nlohmann::ordered_json等其他类型。
二、代码实例
以下代码使用NLOHMANN_DEFINE_TYPE_INTRUSIVE宏实现读写配置文件output.json。初次使用请先下载nlohmann::json库,将json.hpp放置在
/usr/local/include路径或者其它路径下,并且需要安装好boost库。
1.main.cpp
#include <iostream>
#include <boost/filesystem.hpp>
#include <nlohmann/json.hpp>
#define debug_info(x) std::cout << (x) << std::endl
#define debug_var(x) std::cout << #x << "=" << (x) << std::endl
using json = nlohmann::json;
class QrDataSt {
public:
QrDataSt () = default;
QrDataSt(int pose_id, int qr_id, double qr_x, double qr_y, double qr_deg) :
poseIndex(pose_id), qrIndex(qr_id), qrX(qr_x), qrY(qr_y), qrDeg(qr_deg) {
}
void print() {
debug_var(poseIndex);
debug_var(qrIndex);
debug_var(qrX);
debug_var(qrY);
debug_var(qrDeg);
}
NLOHMANN_DEFINE_TYPE_INTRUSIVE(QrDataSt, poseIndex, qrIndex, qrX, qrY, qrDeg)
private:
int poseIndex{};
int qrIndex{};
double qrX{};
double qrY{};
double qrDeg{};
};
class RobotDataSt {
public:
RobotDataSt() = default;
explicit RobotDataSt(std::vector<QrDataSt> &data) : qrDataSt(data) {
}
void print() {
for (auto item : qrDataSt) {
item.print();
}
}
NLOHMANN_DEFINE_TYPE_INTRUSIVE(RobotDataSt, qrDataSt);
private:
std::vector<QrDataSt> qrDataSt;
};
bool writeJson(const std::string &filename, RobotDataSt &data) {
json j = data;
std::ofstream out(filename);
if (out.is_open()) {
if (out.good()) {
out << j.dump(4) << std::endl; //格式化输出
} else {
debug_info("流错误!");
}
out.flush();
out.close();
} else {
debug_info("打开文件失败:" + filename);
return false;
}
return true;
}
bool readJson(const std::string &filename, RobotDataSt &data)
{
json j;
std::ifstream in(filename);
if (!in.is_open()) {
debug_info("打开文件失败:" + filename);
return false;
}
try {
in >> j;
} catch (json::exception &e) {
debug_var(e.what());
in.close();
return false;
}
data = j.get<RobotDataSt>();
return true;
}
int main() {
const std::string outFilename("./output.json");
QrDataSt mQrDataSt1(1, 110, 955, -939.167, 0);
QrDataSt mQrDataSt2(2, 120, 2149.88, -344.329, 0);
std::vector<QrDataSt> mQrDataBuffer;
mQrDataBuffer.reserve(2);
mQrDataBuffer.emplace_back(mQrDataSt1);
mQrDataBuffer.emplace_back(mQrDataSt2);
// 序列化
RobotDataSt outData(mQrDataBuffer);
writeJson(outFilename, outData);
// 反序列化
RobotDataSt inData;
readJson(outFilename, inData);
inData.print();
return 0;
}
2.CMakeLists.txt
cmake_minimum_required(VERSION 3.10.0)
project(jsonParser)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 14)
find_package(Boost REQUIRED COMPONENTS
filesystem)
file(GLOB SRCS ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
add_executable(${PROJECT_NAME} ${SRCS})
target_link_libraries(${PROJECT_NAME}
${Boost_LIBRARIES})
3.output.json
{
"qrDataSt": [
{
"poseIndex": 1,
"qrDeg": 0.0,
"qrIndex": 110,
"qrX": 955.0,
"qrY": -939.167
},
{
"poseIndex": 2,
"qrDeg": 0.0,
"qrIndex": 120,
"qrX": 2149.88,
"qrY": -344.329
}
]
}
4.终端输出
/home/test/code/exercise/readJson/build/jsonParser
poseIndex=1
qrIndex=110
qrX=955
qrY=-939.167
qrDeg=0
poseIndex=2
qrIndex=120
qrX=2149.88
qrY=-344.329
qrDeg=0
Process finished with exit code 0
5.代码详解
观察output.json可知,文件存储着一个RobotDataSt类型数据,而RobotDataSt类型又包含两个QrDataSt类型的数据。
代码工作流程是先将定义好的数据写入到json文件,再读取打印至终端。其中读写利用c++输入输出流实现,关键的RobotDataSt类型数据序列化实质上利用to_json友元方法自动实现序列化,
同理,Json类型数据反序列化利用from_json友元方法实现。
三、问题
假设RobotDataSt还需新增一个CameraDataSt类型的数据结构,是否可以使用struct自定义一个类型直接添加到RobotDataSt中使用?
struct CameraDataSt {
CameraType type;
std::string username;
std::string password;
};
答案是否定的,如果新增的数据结构不按照(1)宏使用规则定义是无法使用的。
四、拓展
如果使用(1)宏,构造一个不完整的Json数据进行反序列化,则输出将会抛出json.exception.out_of_range.403异常,因此可使用(2)宏,缺失的数据进行反序列化时将自动填充默认值。
五、官方链接
https://github.com/nlohmann/json
https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/