250 lines
6.0 KiB
C++
250 lines
6.0 KiB
C++
#ifndef CJSONFILE_H
|
||
#define CJSONFILE_H
|
||
|
||
#include "_nlohmann_json_wrapper.h"
|
||
#include <fstream>
|
||
#include <iomanip>
|
||
#include <filesystem>
|
||
#include <atlstr.h>
|
||
namespace fs = std::filesystem;
|
||
/** @addtogroup ksjson
|
||
* @{
|
||
* @addtogroup files
|
||
* @brief json/bson file classes.
|
||
* @{ */
|
||
|
||
/**
|
||
* @brief The CJsonFile class
|
||
*/
|
||
class CJsonFile // header only class
|
||
{
|
||
public:
|
||
|
||
CJsonFile()
|
||
:needSave(0)
|
||
{
|
||
|
||
}
|
||
|
||
/// \brief 构造函数
|
||
/// \param filepath 指定文件绝对路径,只保存了文件名,并没有打开文件。
|
||
/// 如果未设置,后续必须通过setFilePath()指定文件的绝对路径
|
||
template<typename T>
|
||
CJsonFile(T filepath)
|
||
{
|
||
setFilePath(filepath);
|
||
}
|
||
|
||
/// 设置文件路径
|
||
/// \param path 文件的绝对路径
|
||
|
||
void setFilePath(const char* path) { _filePath = path; }
|
||
void setFilePath(const wchar_t* path) { _filePath = path; }
|
||
void setFilePath(const fs::path& path) { _filePath = path; }
|
||
#ifdef UNICODE
|
||
void setFilePath(const CString& path) { _filePath = (const wchar_t*)path; }
|
||
#endif
|
||
|
||
// 判断是否设置了路径,没有判断路径是否有效
|
||
bool hasFilePath() const { return !_filePath.empty(); }
|
||
|
||
bool exists() const { return !_filePath.empty() && fs::exists(_filePath); }
|
||
|
||
virtual ~CJsonFile() = default;
|
||
|
||
protected:
|
||
/// 参数文件完整路径。可以在构造函数里指定,或用setPath()指定
|
||
fs::path _filePath;
|
||
|
||
public:
|
||
/// 文件内容与内存变量之间中转的json格式cache。
|
||
jsonobj cache;
|
||
|
||
/// 标记参数是否需要保存。
|
||
int needSave = 0;
|
||
|
||
// 错误字符串
|
||
std::string errStr;
|
||
|
||
/// 清空json缓存
|
||
void clearCache() { cache = jsonobj(); }
|
||
|
||
/// 获取文件路径
|
||
const fs::path& filePath() const { return _filePath; }
|
||
|
||
void reset() {
|
||
cache.clear();
|
||
_filePath.clear();
|
||
}
|
||
|
||
enum {
|
||
jFileNotExist=0,
|
||
jFileOk =1,
|
||
jFileFailOpenRead=-1,
|
||
jFileFailOpenWrite=-2,
|
||
jFileParseError=-3,
|
||
jFileNull=-4,
|
||
jFileOutputError=-5,
|
||
jfileJsonToStructError=-6,
|
||
jfileJsonFromStructError = -7,
|
||
};
|
||
|
||
// ----保存和加载的函数相关 -----
|
||
/// 从文件加载json/bson到cache,加载后关闭文件。
|
||
/// 需要在构造函数中或用setFilePath()设置文件的完整路径
|
||
/// \return 1: 文件打开ok,解析json正确;0:文件打开失败;-1:文件打开成功,解析json出错
|
||
virtual int load()
|
||
{
|
||
// 调用处必须保证已经设置了路径
|
||
_ASSERT(!_filePath.empty());
|
||
|
||
if (!fs::exists(_filePath)) {
|
||
errStr = fmt::format("json文件不存在:{}", _filePath.string());
|
||
return jFileNotExist;
|
||
}
|
||
|
||
std::ifstream in(_filePath);
|
||
if (in.is_open()) {
|
||
try {
|
||
in >> cache;
|
||
needSave = 0;
|
||
in.close();
|
||
return jFileOk;
|
||
}
|
||
catch (jsonobj::exception& e) {
|
||
//throw parse_error.101 in case of an unexpected token
|
||
//throw parse_error.102 if to_unicode fails or surrogate error
|
||
//throw parse_error.103 if to_unicode fails
|
||
errStr = fmt::format("解析json出错:{}\n{}", _filePath.string(), e.what());
|
||
in.close();
|
||
return jFileParseError;
|
||
}
|
||
}
|
||
else {
|
||
errStr = fmt::format("json文件只读打开失败:{}", _filePath.string());
|
||
return jFileFailOpenRead;
|
||
}
|
||
}
|
||
|
||
/// 保存cache到指定的文件,覆盖文件内容,写完后关闭文件,但内存中保存json格式的cache。
|
||
/// 需要在构造函数中或用setFilePath()设置文件的完整路径。
|
||
/// \return 1: 文件保存成功;0: 文件创建失败;-1: json中有字符串不是utf-8
|
||
virtual int save()
|
||
{
|
||
// 调用处必须保证已经设置了路径
|
||
_ASSERT(!_filePath.empty());
|
||
|
||
std::ofstream o(_filePath, std::ofstream::trunc);
|
||
if (o.is_open()) {
|
||
try {
|
||
o << std::setw(4) << cache << std::endl;
|
||
o.flush();
|
||
needSave = 0;
|
||
return jFileOk;
|
||
}
|
||
catch (jsonobj::exception& e) {
|
||
// throw type_error.316 if a string stored inside the JSON value is not UTF-8 encoded
|
||
errStr = fmt::format("转换json文本出错:{}\n{}", _filePath.string(), e.what());
|
||
return jFileOutputError;
|
||
}
|
||
}
|
||
else {
|
||
errStr = fmt::format("json文件写入打开失败:{}", _filePath.string());
|
||
return jFileFailOpenWrite;
|
||
}
|
||
|
||
}
|
||
|
||
template <typename T>
|
||
int save(const T& src, bool clearBeforeSave=false) {
|
||
if (clearBeforeSave) clearCache();
|
||
try {
|
||
to_json(cache, src);
|
||
}
|
||
catch (jsonobj::exception& e) {
|
||
errStr = fmt::format("json<= 结构体出错:{}\n{}", _filePath.string(), e.what());
|
||
return jfileJsonFromStructError;
|
||
}
|
||
|
||
return save();
|
||
}
|
||
|
||
template <typename T>
|
||
int load(T& dst) {
|
||
|
||
errStr.clear();
|
||
int ret = jFileOk;
|
||
do {
|
||
ret = load();
|
||
if (ret != 1) break;
|
||
if (cache.is_null()) {
|
||
errStr = fmt::format("读到的json是空的:{}", _filePath.string());
|
||
ret = jFileNull;
|
||
break;
|
||
}
|
||
try {
|
||
from_json(cache, dst);
|
||
}
|
||
catch (jsonobj::exception& e) {
|
||
errStr = fmt::format("json => 结构体出错:{}\n{}", _filePath.string(), e.what());
|
||
ret = jfileJsonToStructError;
|
||
break;
|
||
}
|
||
return jFileOk;
|
||
} while (0);
|
||
alog->error(errStr);
|
||
return ret;
|
||
}
|
||
|
||
/// 将cache中名为name的部分转到结构体T的实例dst
|
||
template <typename T>
|
||
int copyToStruct(const char* name, T& dst)
|
||
{
|
||
if (cache.contains(name)) {
|
||
try {
|
||
cache[name].get_to<T>(dst);
|
||
}
|
||
catch (jsonobj::exception& e) {
|
||
errStr = fmt::format("json {} => 结构体出错:{}\n{}", name, _filePath.string(), e.what());
|
||
return jfileJsonToStructError;
|
||
}
|
||
return 1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/// 将结构体T的实例src内容转成json保存到cache中名为name的字段
|
||
template <typename T>
|
||
void saveToCache(const char* name, const T& src)
|
||
{
|
||
try {
|
||
cache[name] = src;
|
||
}
|
||
catch (jsonobj::exception& e) {
|
||
errStr = fmt::format("json {} <= 结构体出错:{}\n{}", name, _filePath.string(), e.what());
|
||
return jfileJsonFromStructError;
|
||
}
|
||
needSave++;
|
||
}
|
||
};
|
||
|
||
class CBsonFile : public CJsonFile
|
||
{
|
||
public:
|
||
/// 重载load函数,将json改成bson。
|
||
virtual int load()
|
||
{
|
||
return 1;
|
||
}
|
||
|
||
/// 重载load函数,将json改成bson。
|
||
virtual int save()
|
||
{
|
||
return 1;
|
||
}
|
||
};
|
||
|
||
/** @} files */
|
||
/** @} ksjson */
|
||
#endif // CJSONFILE_H
|