封装目标
- 能够将
toml 文件中数据转为的 QVariantHash 和 QVariantMap 的结构
- 能够将
QVariantHash 和 QVariantMap 写入到toml文件中
- 开发语言: C++
- 提供源码, 为再开发提供遍历
- 愉快的使用key-value 键值对了
依赖
自封装库源码
库名称
QTomlplusplus.h 头文件
///
/// @file QTomlplusplus.h
/// @author oct (oct.outlook.com)
/// @brief 基于tomlplusplus库的QTomlplusplus类, 用于读写QVariantHash和QVariantMap格式的TOML文件
/// @version 0.1
/// @date 2025-06-14
///
/// @copyright Copyright (c) 2025
///
///
#pragma once
#include <QObject>
#include <QHash>
#include <QVariant>
#include <QList>
#include <QString>
#include "toml.h"
///
/// @brief 基于tomlplusplus库的QTomlplusplus类, 用于读写和解析TOML格式的文件
///
class QTomlplusplus : public QObject
{
Q_OBJECT;
public:
explicit QTomlplusplus(QObject *parent = nullptr);
virtual ~QTomlplusplus();
///
/// @brief: 解码toml格式的文件内容为QVariantHash
/// @param: targetFile 将要解析的是哪个文件
/// @param: ourErrorString 错误信息
/// @return: QT_NAMESPACE::QVariantHash
///
int parseFileToHash(const QString& targetFile, QVariantHash* outHash, QString* ourErrorString);
///
/// @brief: 解码toml格式的文件内容为QVariantMap
/// @param: targetFile
/// @return: QT_NAMESPACE::QVariantMap
///
int parseFileToMap(const QString& targetFile, QVariantMap* outHash, QString* ourErrorString);
///
/// @brief: 将参数1的内容按照toml写入到参数2的文件中
/// @param: targetHash 将要写入的文件结构
/// @param: targetFile 写入到哪个文件中,注意,文件路径需要存在,比如: A/B/C/D/E/tomlFile.toml, A/B/C/D/E路径需要存在,否则可能无法写入文件。
/// @return: void
///
int writeHashToToml(QVariantHash& targetHash, const QString& targetFile, QString* outErrorString);
///
/// @brief: 将参数1的内容按照toml写入到参数2的文件中
/// @param: targetMap 将要写入的文件结构
/// @param: targetFile 写入到哪个文件中,注意,文件路径需要存在,比如: A/B/C/D/E/tomlFile.toml, A/B/C/D/E路径需要存在,否则可能无法写入文件。
/// @return: void
///
int writeMapToToml(QVariantMap& targetMap, const QString& targetFile, QString* outErrorString);
private:
/// @brief Qstring 转 std::string
/// @param str - QString
/// @return std::string
std::string qstr2str(const QString& str);
///
/// 检查文件状态是否OK
/// @brief: fileStateIsOk
/// @param: fileName
/// @return: bool
///
[[nodiscard]] bool canReadFile(const QString& fileName, QString* outErrorString);
///
/// @brief: 解析数组
/// @param: array
/// @return: QT_NAMESPACE::QVariantList
///
QVariantList tomlArrayToQVariantList(const toml::array* arrayTmp);
///
/// @brief: 解析单个节点
/// @param: node
/// @return: QT_NAMESPACE::QVariant
///
QVariant tomlNodeToQVariant(const toml::node* nodeTmp);
///
/// @brief: 解析table
/// @param: table
/// @return: QT_NAMESPACE::QVariantHash
///
QVariantHash tomlTableToQVariantHash(const toml::table* tableTmp);
///
/// @brief: 转为table
/// @param: hash
/// @param: table
/// @return: void
///
void qVariantToTomlTable(const QVariantHash& hash, toml::table* tableTmp);
///
/// @brief: 转为table
/// @param: hash
/// @param: table
/// @return: void
///
void qVariantMapToTomlTable(const QVariantMap& hash, toml::table* tableTmp);
///
/// @brief: 转为array
/// @param: list
/// @param: array
/// @return: void
///
void qVariantToTomlArray(const QVariantList& list, toml::array* arrayTmp);
};
QTomlplusplus.cpp 头文件
#include "QTomlplusplus.h"
#include <QHash>
#include <QMap>
#include <QVariant>
#include <QTime>
#include <QDateTime>
#include <QFile>
QTomlplusplus::QTomlplusplus(QObject *parent)
: QObject(parent)
{}
QTomlplusplus::~QTomlplusplus()
{}
///
/// @brief: parseFileToHash
/// @param: targetFile
/// @param: outHash
/// @param: ourErrorString
/// @return: int
///
int QTomlplusplus::parseFileToHash(const QString& targetFile, QVariantHash* outHash, QString* ourErrorString)
{
if (false == canReadFile(targetFile, ourErrorString))
{
return 1;
}
QVariantHash& result = *outHash;
/// 解析文件中的数据
toml::parse_result tableData = toml::parse_file(qstr2str(targetFile));
for (const auto& [key, value] : tableData)
{
QString qKey = QString::fromStdString(std::string(key));
result[qKey] = tomlNodeToQVariant(&value);
}
return 0;
}
///
/// @brief: parseFileToMap
/// @param: targetFile
/// @param: outHash
/// @param: ourErrorString
/// @return: int
///
int QTomlplusplus::parseFileToMap(const QString& targetFile, QVariantMap* outHash, QString* ourErrorString)
{
if (false == canReadFile(targetFile, ourErrorString))
{
return 1;
}
QVariantMap& result = *outHash;
/// 解析文件中的数据
toml::parse_result tableData = toml::parse_file(qstr2str(targetFile));
for (const auto& [key, value] : tableData)
{
QString qKey = QString::fromLocal8Bit(QByteArray(key.data(), key.length()));
result[qKey] = tomlNodeToQVariant(&value);
}
return 0;
}
///
/// @brief: writeToToml
/// @param: targetHash
/// @param: targetFile
/// @return: void
///
int QTomlplusplus::writeHashToToml(QVariantHash& targetHash, const QString& targetFile, QString* outErrorString)
{
toml::table rootTable{};
qVariantToTomlTable(targetHash, &rootTable);
std::ofstream file(qstr2str(targetFile));
if (!file.is_open())
{
*outErrorString = QString::fromLocal8Bit(QByteArray(strerror(errno)));
return 1;
}
file << rootTable;
file.flush();
file.close();
return 0;
}
///
/// @brief: writeMapToToml
/// @param: targetMap
/// @param: targetFile
/// @param: outErrorString
/// @return: int
///
int QTomlplusplus::writeMapToToml(QVariantMap& targetMap, const QString& targetFile, QString* outErrorString)
{
toml::table rootTable{};
qVariantMapToTomlTable(targetMap, &rootTable);
std::ofstream file(qstr2str(targetFile));
if (!file.is_open())
{
*outErrorString = QString::fromLocal8Bit(QByteArray(strerror(errno)));
return 1;
}
file << rootTable;
file.flush();
file.close();
return 0;
}
///
/// @brief: qstr2str
/// @param: str
/// @return: std::string
///
std::string QTomlplusplus::qstr2str(const QString& str)
{
std::string ret = str.toLocal8Bit().constData();
return ret;
}
///
/// @brief: fileStateIsOk
/// @param: fileName
/// @return: bool
///
bool QTomlplusplus::canReadFile(const QString& fileName, QString* outErrorString)
{
QFile tmpFile(fileName);
/// 文件不存在
if (false == tmpFile.exists())
{
*outErrorString = QString("The file, %1, doesnt exist").arg(fileName);
return false;
}
return true;
}
///
/// @brief: tomlArrayToQVariantList
/// @param: array
/// @return: QT_NAMESPACE::QVariantList
///
QVariantList QTomlplusplus::tomlArrayToQVariantList(const toml::array* arrayTmp)
{
const toml::array& array = *arrayTmp;
QVariantList result{};
for (const auto& item : array)
{
result.append(tomlNodeToQVariant(&item));
}
return result;
}
///
/// @brief: tomlNodeToQVariant
/// @param: node
/// @return: QT_NAMESPACE::QVariant
///
QVariant QTomlplusplus::tomlNodeToQVariant(const toml::node* nodeTmp)
{
switch (nodeTmp->type())
{
case toml::node_type::table:
return tomlTableToQVariantHash((nodeTmp->as_table()));
case toml::node_type::array:
return tomlArrayToQVariantList(nodeTmp->as_array());
case toml::node_type::string:
return QString::fromStdString(nodeTmp->value_or<std::string>(""));
case toml::node_type::integer:
return static_cast<qlonglong>(*nodeTmp->as_integer());
case toml::node_type::floating_point:
return nodeTmp->value_or<double>(0.0);
case toml::node_type::boolean:
return nodeTmp->value_or<bool>(false);
case toml::node_type::date:
{
toml::date tmpDate = nodeTmp->value_or<toml::date>(toml::date());
return QDate(
tmpDate.year,
tmpDate.month,
tmpDate.day
);
}
case toml::node_type::time:
{
toml::time tmpTime = nodeTmp->value_or<toml::time>(toml::time());
return QTime(tmpTime.hour, tmpTime.minute, tmpTime.second, tmpTime.nanosecond / 1000000);
}
case toml::node_type::date_time:
{
toml::date_time tmpDateTime = nodeTmp->value_or<toml::date_time>(toml::date_time());
return QDateTime(QDate(tmpDateTime.date.year, tmpDateTime.date.month, tmpDateTime.date.day),
QTime(tmpDateTime.time.hour, tmpDateTime.time.minute,
tmpDateTime.time.second, tmpDateTime.time.nanosecond / 1000000));
}
default:
return QVariant();
}
}
///
/// @brief: qVariantToTomlTable
/// @param: table 将要转为hash的table
/// @return: void
///
QVariantHash QTomlplusplus::tomlTableToQVariantHash(const toml::table* tableTmp)
{
const toml::table& table = *tableTmp;
QVariantHash result{};
for (const auto& [key, value] : table)
{
QString qKey = QString::fromStdString(std::string(key));
result[qKey] = tomlNodeToQVariant(&value);
}
return result;
}
///
/// @brief: qVariantToTomlTable
/// @param: hash
/// @param: table
/// @return: void
///
void QTomlplusplus::qVariantToTomlTable(const QVariantHash& hash, toml::table* tableTmp)
{
toml::table& table = *tableTmp;
for (auto it = hash.begin(); it != hash.end(); ++it)
{
const std::string key = qstr2str(it.key());
const QVariant& value = it.value();
switch (value.type())
{
case QMetaType::QVariantHash:
{
toml::table subTable;
qVariantToTomlTable(value.toHash(), &subTable);
table.insert(key, std::move(subTable));
break;
}
case QMetaType::QVariantList:
{
toml::array array;
qVariantToTomlArray(value.toList(), &array);
table.insert(key, std::move(array));
break;
}
case QMetaType::QString:
{
toml::table tbl;
QString tmpQString = value.toString();
std::string tmpStr = tmpQString.toStdString();
table.insert(key, tmpStr);
}
break;
case QMetaType::LongLong:
case QMetaType::Int:
table.insert(key, value.toLongLong());
break;
case QMetaType::Double:
table.insert(key, value.toDouble());
break;
case QMetaType::Bool:
table.insert(key, value.toBool());
break;
case QMetaType::QDate:
{
QDate date = value.toDate();
table.insert(key, toml::date(date.year(), date.month(), date.day()));
break;
}
case QMetaType::QTime:
{
QTime time = value.toTime();
table.insert(key, toml::time(time.hour(), time.minute(), time.second(), time.msec() * 1000000));
break;
}
case QMetaType::QDateTime:
{
QDateTime dt = value.toDateTime();
table.insert(key, toml::date_time(
toml::date(dt.date().year(), dt.date().month(), dt.date().day()),
toml::time(dt.time().hour(), dt.time().minute(), dt.time().second(), dt.time().msec() * 1000000)
));
break;
}
default:
// 处理未知类型或转换为字符串
table.insert(key, qstr2str(value.toString()));
break;
}
}
}
///
/// @brief: qVariantMapToTomlTable
/// @param: hash
/// @param: tableTmp
/// @return: void
///
void QTomlplusplus::qVariantMapToTomlTable(const QVariantMap& hash, toml::table* tableTmp)
{
toml::table& table = *tableTmp;
for (auto it = hash.begin(); it != hash.end(); ++it)
{
const std::string key = qstr2str(it.key());
const QVariant& value = it.value();
const QVariant::Type valueType = value.type();
switch (valueType)
{
case QMetaType::QVariantHash:
case QMetaType::QVariantMap:
{
toml::table subTable;
qVariantMapToTomlTable(value.toMap(), &subTable);
table.insert(key, std::move(subTable));
break;
}
case QMetaType::QVariantList:
{
toml::array array;
qVariantToTomlArray(value.toList(), &array);
table.insert(key, std::move(array));
break;
}
case QMetaType::QString:
{
toml::table tbl;
QString tmpQString = value.toString();
std::string tmpStr = tmpQString.toStdString();
table.insert(key, tmpStr);
}
break;
case QMetaType::LongLong:
case QMetaType::Int:
table.insert(key, value.toLongLong());
break;
case QMetaType::Double:
table.insert(key, value.toDouble());
break;
case QMetaType::Bool:
table.insert(key, value.toBool());
break;
case QMetaType::QDate:
{
QDate date = value.toDate();
table.insert(key, toml::date(date.year(), date.month(), date.day()));
break;
}
case QMetaType::QTime:
{
QTime time = value.toTime();
table.insert(key, toml::time(time.hour(), time.minute(), time.second(), time.msec() * 1000000));
break;
}
case QMetaType::QDateTime:
{
QDateTime dt = value.toDateTime();
table.insert(key, toml::date_time(
toml::date(dt.date().year(), dt.date().month(), dt.date().day()),
toml::time(dt.time().hour(), dt.time().minute(), dt.time().second(), dt.time().msec() * 1000000)
));
break;
}
default:
// 处理未知类型或转换为字符串
table.insert(key, qstr2str(value.toString()));
break;
}
}
}
///
/// @brief: qVariantToTomlArray
/// @param: list
/// @param: array
/// @return: void
///
void QTomlplusplus::qVariantToTomlArray(const QVariantList& list, toml::array* arrayTmp)
{
toml::array& array = *arrayTmp;
for (const QVariant& item : list)
{
switch (item.type())
{
case QMetaType::QVariantHash:
{
toml::table subTable;
qVariantToTomlTable(item.toHash(), &subTable);
array.push_back(std::move(subTable));
break;
}
case QMetaType::QVariantList:
{
toml::array subArray;
qVariantToTomlArray(item.toList(), &subArray);
array.push_back(std::move(subArray));
break;
}
case QMetaType::QString:
array.push_back(qstr2str(item.toString()));
break;
case QMetaType::LongLong:
case QMetaType::Int:
array.push_back(item.toLongLong());
break;
case QMetaType::Double:
array.push_back(item.toDouble());
break;
case QMetaType::Bool:
array.push_back(item.toBool());
break;
case QMetaType::QDate:
{
QDate date = item.toDate();
array.push_back(toml::date(date.year(), date.month(), date.day()));
break;
}
case QMetaType::QTime:
{
QTime time = item.toTime();
array.push_back(toml::time(time.hour(), time.minute(), time.second(), time.msec() * 1000000));
break;
}
case QMetaType::QDateTime:
{
QDateTime dt = item.toDateTime();
array.push_back(toml::date_time(
toml::date(dt.date().year(), dt.date().month(), dt.date().day()),
toml::time(dt.time().hour(), dt.time().minute(), dt.time().second(), dt.time().msec() * 1000000)
));
break;
}
default:
// 处理未知类型或转换为字符串
array.push_back(qstr2str(item.toString()));
break;
}
}
}
用法
写入, 将 内存中QVariantHash的变量值写入到文件
/// 准备将要写入的数据
QVariantMap targetConfigMap{{"1", QVariant(1)}, {"2", QVariant(2)}};
/// 保存错误信息
QString errorString{""};
/// 创建读写对象
QTomlplusplus qtoml{};
/// 目标文件: toml, 比如: A:/B/C/D.toml
const QString targetFile = preferenceRoot + tc_config::sGlobalConfigToml;
/// 调用写入接口
int retValue = qtoml.writeMapToToml(targetConfigMap, targetFile, &errorString);
if (0 != retValue)
{
/// 写入出现了错误
qDebug() << "errorString=" << errorString;
}
读取,将从 toml 文件读取的文件内容保存到 QVariantHash的变量
/// 创建读写对象
QTomlplusplus qtoml{};
/// 保存错误信息
QString errorString{};
/// 定义保存文件中的变量
QVariantMap configMap{};
/// 从哪个文件中读取数据, 比如: A:/B/C/D.toml
const QString targetFile = preferenceRoot + tc_config::sGlobalConfigToml;
qtoml.parseFileToMap(targetFile, &configMap, &errorString);