#ifndef CJSONFILE_H #define CJSONFILE_H #include "_nlohmann_json_wrapper.h" #include #include #include #include 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 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 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 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 int copyToStruct(const char* name, T& dst) { if (cache.contains(name)) { try { cache[name].get_to(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 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