422 lines
13 KiB
C++
422 lines
13 KiB
C++
#ifndef JSONREADWITHVERIFY_H
|
||
#define JSONREADWITHVERIFY_H
|
||
|
||
#include "_nlohmann_json_wrapper.h"
|
||
#include "ParaHelper.h"
|
||
#include "alog.h"
|
||
|
||
/** @addtogroup ksjson
|
||
* @{
|
||
* @addtogroup readWithVerify
|
||
* @brief 从json转换到数值前判断key是否存在,确认数值是否在一定[min, max]范围内,或在一个数组列表范围之内。
|
||
* @{
|
||
*/
|
||
|
||
/// \brief 尝试从obj读取键值为key的数据,没找到不赋值返回false,找到就给指针赋值并返回true。
|
||
/// \details 通常用于已经初始化为默认值的变量或结构体,找到就读,没找到就保持默认值。
|
||
/// 注意保存的格式要跟写入的格式一致,别写个string当int读,肯定会出问题的。这里就没法检验格式是否正确了。
|
||
///
|
||
/// \param obj nlohmann::json对象
|
||
/// \param key 键值,类型KeyT支持类型const char *, std::string
|
||
/// \param dst 目标变量,类型ValueT可以是所有支持的数据类型,包括nlohmann::json标准的数据类型和定义了to_json和from_json的类型。
|
||
/// \return 没找到返回0,找到返回1。
|
||
template <typename KeyT, typename = typename JsonKey<KeyT>::Type, typename ValueT>
|
||
int tryRead(const jsonobj& obj, KeyT key, ValueT& dst)
|
||
{
|
||
auto it = obj.find(key);
|
||
if (it == obj.end() || it->is_null()) return 0; //没找到
|
||
it->template get_to<ValueT>(dst);
|
||
return 1;
|
||
}
|
||
|
||
/// 尝试从obj读取键值为key的数据,没找到设置为缺省值并返回false,找到就读取数值给指针赋值并返回true。
|
||
/// 类型ValueT可以是所有支持的数据类型,包括nlohmann::json标准的数据类型和定义了to_json和from_json的类型。
|
||
/// \param obj nlohmann::json对象
|
||
/// \param dst 目标变量。
|
||
/// \param key 键值,类型KeyT支持类型const char *, std::string
|
||
/// \param def 找不到键值使用的缺省值。def的数据类型ValueT2必须与dst的数据类型ValueT可以直接隐式转换或复制构造的
|
||
/// \return 没找到返回false,找到返回true。
|
||
template <typename KeyT, typename = typename JsonKey<KeyT>::Type, typename ValueT, typename ValueT2>
|
||
int readWithDefault(const jsonobj& obj, KeyT key, ValueT& dst, ValueT2 def)
|
||
{
|
||
auto it = obj.find(key);
|
||
if (it == obj.end() || it->is_null()) {//没找到
|
||
dst = def;
|
||
return 0;
|
||
}
|
||
it->template get_to<ValueT>(dst);
|
||
return 1;
|
||
}
|
||
|
||
/// \brief 在\ref readWithDefault 基础上增加了校验读到的值是否在[min, max]范围之内。
|
||
/// \details 类型ValueT通常是数值类型。其它支持的数据类型如果它支持用大于号/小于号比较的类型也行,估计很少会用到。
|
||
/// \param obj nlohmann::json对象
|
||
/// \param key 键值,类型KeyT支持类型const char *, std::string
|
||
/// \param dst 目标变量。
|
||
/// \param def 找不到键值使用的缺省值。def的数据类型必须与dst的数据类型可以直接转换的
|
||
/// \param min 取值范围的下限,min << value << max
|
||
/// \param max 取值范围的上限,min << value << max
|
||
/// \return 返回读取状态。
|
||
/// 没找到返回0,找到但数值超界返回-1,找到并在有效范围内返回1。
|
||
template <typename KeyT, typename = typename JsonKey<KeyT>::Type, typename ValueT, typename ValueT2, typename ValueT3>
|
||
int readInRange(const jsonobj& obj, KeyT key, ValueT& dst, const ValueT2& def, const ValueT3& min, const ValueT3& max)
|
||
{
|
||
auto it = obj.find(key);
|
||
if (it == obj.end() || it->is_null()) {//没找到
|
||
dst = ValueT(def);
|
||
return 0;
|
||
}
|
||
ValueT tmp = it->template get<ValueT>();
|
||
if (tmp < ValueT(min)) {
|
||
dst = ValueT(min);
|
||
return -1;
|
||
}
|
||
else if (tmp > ValueT(max)) {//超界
|
||
dst = ValueT(max);
|
||
return -1;
|
||
}
|
||
// 返回读取值
|
||
dst = tmp;
|
||
return 1;
|
||
}
|
||
|
||
/// \brief 重载\ref readInRange 用sParaRange<ValueT>做取值范围的校验。
|
||
/// \details 用sParaRange的成员变量Def, Min, Max替代\ref readInRange的def, min, max参数
|
||
template <typename KeyT, typename = typename JsonKey<KeyT>::Type, typename ValueT>
|
||
inline int readInRange(const jsonobj& obj, KeyT key, ValueT& dst, const sParaRange<ValueT>& range)
|
||
{
|
||
return readInRange(obj, key, dst, range.Def, range.Min, range.Max);
|
||
}
|
||
|
||
/// \brief json --> ValueT,并校验数值是否在数组list[num]范围类
|
||
/// \details
|
||
/// - 没找到:返回值为0,dst设置为def<br>
|
||
/// - 找到但数值布置在列表范围内:返回值-1,dst设置为def<br>
|
||
/// - 找到并在列表范围内:返回1,dst设置为解析值。<br>
|
||
template <typename KeyT, typename = typename JsonKey<KeyT>::Type, typename ValueT, typename NumT, typename = ArraySizeT<NumT>::Type>
|
||
int readInList(const jsonobj& obj, KeyT key, ValueT& dst, const ValueT& def, const ValueT* list, NumT num)
|
||
{
|
||
auto it = obj.find(key);
|
||
if (it == obj.end() || it->is_null()) {//没找到
|
||
dst = def;
|
||
return 0;
|
||
}
|
||
ValueT tmp = it->template get<ValueT>();
|
||
for (int i = 0; i < num; i++) { //不在列表中
|
||
if (tmp == list[i]) {
|
||
dst = tmp;
|
||
return 1;
|
||
}
|
||
}
|
||
dst = def;
|
||
|
||
return -1;
|
||
}
|
||
|
||
/// \brief json --> ValueT,并校验数值是否在QVector<ValueT>列表范围类
|
||
/// \details
|
||
/// - 没找到:返回值为0,dst设置为def<br>
|
||
/// - 找到但数值布置在列表范围内:返回值-1,dst设置为def<br>
|
||
/// - 找到并在列表范围内:返回1,dst设置为解析值。<br>
|
||
template <typename KeyT, typename = typename JsonKey<KeyT>::Type, typename ValueT>
|
||
int readInList(const jsonobj& obj, KeyT key, ValueT& dst, const ValueT& def, std::vector<ValueT> list)
|
||
{
|
||
auto it = obj.find(key);
|
||
if (it == obj.end() || it->is_null()) {//没找到
|
||
dst = def;
|
||
return 0;
|
||
}
|
||
ValueT tmp = it->template get<ValueT>();
|
||
if (list.contains(tmp)) {
|
||
dst = tmp;
|
||
return 1;
|
||
}
|
||
dst = def;
|
||
|
||
return -1;
|
||
}
|
||
|
||
/// \brief json --> ValueT,并校验数值是否在QList<ValueT>列表范围类
|
||
/// \details
|
||
/// - 没找到:返回值为0,dst设置为def<br>
|
||
/// - 找到但数值布置在列表范围内:返回值-1,dst设置为def<br>
|
||
/// - 找到并在列表范围内:返回1,dst设置为解析值。<br>
|
||
template <typename KeyT, typename = typename JsonKey<KeyT>::Type, typename ValueT>
|
||
int readInList(const jsonobj& obj, KeyT key, ValueT& dst, const ValueT& def, std::list<ValueT> list)
|
||
{
|
||
auto it = obj.find(key);
|
||
if (it == obj.end() || it->is_null()) {//没找到
|
||
dst = def;
|
||
return 0;
|
||
}
|
||
ValueT tmp = it->template get<ValueT>();
|
||
if (list.contains(tmp)) {
|
||
dst = tmp;
|
||
return 1;
|
||
}
|
||
dst = def;
|
||
|
||
return -1;
|
||
}
|
||
|
||
/// \brief 从json读入string, 与strlist里的字符串比较,确定枚举值。
|
||
/// \details
|
||
/// \param strlist 字符串列表
|
||
/// \param num 字符串个数
|
||
/// \param vallist 枚举值列表。<br>
|
||
/// vallist可以为null,代表枚举值如果是从0开始顺序递增的,dst设置为strlist数组的下标。<br>
|
||
/// \return <br>
|
||
/// - 没找到:返回值为0,dst设置为def<br>
|
||
/// - 找到但字符串不在列表范围内:返回值-1,dst设置为def<br>
|
||
/// - 找到并在列表范围内:返回1。如果vallist为null,dst设置strlist数组的下表;如果vallist非空,dst设置为对于的数组。<br>
|
||
template <typename KeyT, typename = typename JsonKey<KeyT>::Type, typename ValueT>
|
||
int readEnum(const jsonobj& obj, KeyT key, ValueT& dst, int def, const char* const* strlist, int num, const int* vallist = nullptr)
|
||
{
|
||
auto it = obj.find(key);
|
||
if (it == obj.end() || it->is_null()) {//没找到
|
||
alog->warn("readEnum 没找到{},返回缺省值", key);
|
||
dst = ValueT(def);
|
||
return 0;
|
||
}
|
||
if (it->is_string()) {
|
||
std::string str = it->template get<std::string>();
|
||
for (int i = 0; i < num; i++) {
|
||
if (str.compare(strlist[i]) == 0) {
|
||
if (vallist) dst = ValueT(vallist[i]);
|
||
else dst = ValueT(i);
|
||
return 1;
|
||
}
|
||
}
|
||
alog->warn("readEnum {}没有匹配到字符串{},返回缺省值", key);
|
||
dst = ValueT(def);
|
||
}
|
||
else if (it->is_number_integer()) {
|
||
int val = it->template get<int>();
|
||
if (vallist) {
|
||
for (int i = 0; i < num; i++) {
|
||
if (val == vallist[i]) {
|
||
dst = ValueT(val);
|
||
return 1;
|
||
}
|
||
}
|
||
alog->warn("readEnum {}数值不在列表中,返回缺省值", key);
|
||
dst = ValueT(def);
|
||
}
|
||
else {
|
||
if (val >= 0 && val < num) {
|
||
dst = ValueT(val);
|
||
return 1;
|
||
}
|
||
else {
|
||
alog->warn("readEnum {}数值超界,返回缺省值", key);
|
||
dst = ValueT(def);
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
alog->error("{}的json类型错误,应该是字符串或整形,未修改结果", key);
|
||
}
|
||
|
||
return -1;
|
||
}
|
||
|
||
// 与上一版本的区别是没有缺省值
|
||
template <typename KeyT, typename = typename JsonKey<KeyT>::Type, typename ValueT>
|
||
int readEnum(const jsonobj& obj, KeyT key, ValueT& dst, const char* const * strlist, int num, const int* vallist = nullptr)
|
||
{
|
||
auto it = obj.find(key);
|
||
if (it == obj.end() || it->is_null()) {//没找到
|
||
alog->warn("readEnum 没找到{},返回缺省值", key);
|
||
return 0;
|
||
}
|
||
if (it->is_string()) {
|
||
std::string str = it->template get<std::string>();
|
||
for (int i = 0; i < num; i++) {
|
||
if (str.compare(strlist[i]) == 0) {
|
||
if (vallist) dst = ValueT(vallist[i]);
|
||
else dst = ValueT(i);
|
||
return 1;
|
||
}
|
||
}
|
||
alog->warn("readEnum {}没有匹配到字符串{},未赋值", key);
|
||
}
|
||
else if (it->is_number_integer()) {
|
||
int val = it->template get<int>();
|
||
if (vallist) {
|
||
for (int i = 0; i < num; i++) {
|
||
if (val == vallist[i]) {
|
||
dst = ValueT(val);
|
||
return 1;
|
||
}
|
||
}
|
||
alog->warn("readEnum {}数值不在列表中,未赋值", key);
|
||
}
|
||
else {
|
||
if (val >= 0 && val < num) {
|
||
dst = ValueT(val);
|
||
return 1;
|
||
}
|
||
else {
|
||
alog->warn("readEnum {}数值超界,未赋值", key);
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
alog->error("{}的json类型错误,应该是字符串或整形,未修改结果", key);
|
||
}
|
||
|
||
return -1;
|
||
}
|
||
|
||
/// \brief 从json读入string, 与strlist里的字符串比较,确定枚举值。
|
||
/// \details
|
||
/// \param strlist 字符串列表
|
||
/// \param vallist 枚举值列表。<br>
|
||
/// \return <br>
|
||
/// - 没找到:返回值为0,dst设置为def<br>
|
||
/// - 找到但字符串不在列表范围内:返回值-1,dst设置为def<br>
|
||
/// - 找到并在列表范围内:返回1。如果vallist为null,dst设置strlist数组的下表;如果vallist非空,dst设置为对于的数组。<br>
|
||
template <typename KeyT, typename = typename JsonKey<KeyT>::Type, typename ValueT, typename ValutT2>
|
||
int readEnum(const jsonobj& obj, KeyT key, ValueT& dst, ValutT2 def, const char** strlist, const std::vector<ValueT>& vallist)
|
||
{
|
||
auto it = obj.find(key);
|
||
if (it == obj.end() || it->is_null()) {//没找到
|
||
dst = ValueT(def);
|
||
return 0;
|
||
}
|
||
std::string str = it->template get<std::string>();
|
||
for (int i = 0; i < vallist.size(); i++) {
|
||
if (str.compare(strlist[i]) == 0) {
|
||
dst = vallist[i];
|
||
return 1;
|
||
}
|
||
}
|
||
dst = ValueT(def);
|
||
return -1;
|
||
}
|
||
|
||
/// 将C++枚举数组转换为字符串数组写入json
|
||
template <typename KeyT, typename = typename JsonKey<KeyT>::Type, typename ValueT>
|
||
void writeEnumArray(jsonobj& obj, KeyT key, const char* const* strlist, int strnum, const std::vector<ValueT> &valuelist)
|
||
{
|
||
auto arr=jsonobj::array();
|
||
for(auto &val : valuelist) {
|
||
if (val < 0 || val >= strnum) {
|
||
alog->error("writeEnum {}数值超界", key);
|
||
return;
|
||
}
|
||
arr.push_back(strlist[val]);
|
||
}
|
||
obj[key] = arr;
|
||
}
|
||
|
||
template <typename KeyT, typename = typename JsonKey<KeyT>::Type, typename ValueT>
|
||
int readEnumArray(const jsonobj& obj, KeyT key, std::vector<ValueT>& dst, const char* const* strlist, int num)
|
||
{
|
||
auto it = obj.find(key);
|
||
if (it == obj.end()) {//没找到
|
||
alog->warn("readEnumArray 没找到{}", key);
|
||
return 0;
|
||
}
|
||
if (!it->is_array()) {
|
||
alog->error("readEnumArray {} 不是数组", key);
|
||
return 0;
|
||
}
|
||
|
||
if (it->size() == 0) {
|
||
alog->error("readEnumArray {} 是空数组", key);
|
||
return 0;
|
||
}
|
||
|
||
dst.clear();
|
||
for (auto& x : *it) {
|
||
if (x.is_string()) {
|
||
std::string str=x.get<std::string >();
|
||
int i = 0;
|
||
for (; i < num; i++) {
|
||
if (str.compare(strlist[i]) == 0) {
|
||
dst.push_back(ValueT(i));
|
||
break;
|
||
}
|
||
}
|
||
if(i==num )alog->warn("readEnumArray {}没有匹配到字符串{},未赋值", key, str);
|
||
}
|
||
else {
|
||
alog->error("{}的json类型错误,应该是字符串,未修改结果", key);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
return int(dst.size());
|
||
}
|
||
|
||
|
||
template <typename KeyT, typename = typename JsonKey<KeyT>::Type>
|
||
int readStr(const jsonobj& obj, const KeyT& key, char* dst, int dstsize)
|
||
{
|
||
auto it = obj.find(key);
|
||
if (it == obj.end() || it->is_null()) {//没找到,或空
|
||
memset(dst, 0, dstsize);
|
||
return 0;
|
||
}
|
||
if (!it->is_string()) { //不是字符串
|
||
return -1;
|
||
}
|
||
std::string str = it->template get<std::string>();
|
||
auto len = str.length();
|
||
if (len >= dstsize) {
|
||
return -2;
|
||
}
|
||
else if (len == 0) {
|
||
*dst = 0;
|
||
return 1;
|
||
}
|
||
else {
|
||
memcpy(dst, str.c_str(), len);
|
||
*(dst + len) = 0;
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读入char型数值,如果长度刚好填满,不用0结束
|
||
/// </summary>
|
||
/// <typeparam name="KeyT"></typeparam>
|
||
/// <param name="obj"></param>
|
||
/// <param name="key"></param>
|
||
/// <param name="dst"></param>
|
||
/// <param name="dstsize"></param>
|
||
/// <returns></returns>
|
||
template <typename KeyT, typename = typename JsonKey<KeyT>::Type>
|
||
int readCharArray(const jsonobj& obj, const KeyT& key, char* dst, int dstsize, char fill)
|
||
{
|
||
auto it = obj.find(key);
|
||
if (it == obj.end() || it->is_null()) {//没找到,或空
|
||
memset(dst, 0, dstsize);
|
||
return 0;
|
||
}
|
||
if (!it->is_string()) { //不是字符串
|
||
return -1;
|
||
}
|
||
std::string str = it->template get<std::string>();
|
||
auto len = str.length();
|
||
if (len > dstsize) {
|
||
return -2;
|
||
}
|
||
else {
|
||
memcpy(dst, str.c_str(), len);
|
||
if (len < dstsize) {
|
||
memset(dst + len, fill, dstsize - len);
|
||
}
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/** @} readWithCheck*/
|
||
/** @} json*/
|
||
|
||
#endif // JSONREADWITHVERIFY_H
|