Files
GdCpp12/include/json/CJsonFile.h

250 lines
6.0 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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