封装目标
- 能够将
toml
文件中数据转为的 QVariantHash
和 QVariantMap
的结构
- 能够将
QVariantHash
和 QVariantMap
写入到toml文件中
- 开发语言: C++
- 提供源码, 为再开发提供便利
- 愉快的使用key-value 键值对了
现状
- windows: 目前, 已经通过: Qt5.14 + MSVC 16 验证, 读写接口功能均正常。 读写toml各字段(时间、日期、字符串等,未充分测试, 请指正 😄)
- linux: 待验证
- macos: 待验证
依赖
- tomlplusplus: https://github.com/marzer/tomlplusplus
toml语法
自封装库源码
库名称
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:////// 检查文件状态是否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 "toml.h"
//#include "toml.hpp"
#include "StringConverter.hpp"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(td::StringConverter::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(td::StringConverter::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(td::StringConverter::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(td::StringConverter::qstr2str(targetFile));if (!file.is_open()){*outErrorString = QString::fromLocal8Bit(QByteArray(strerror(errno)));return 1;}file << rootTable;file.flush();file.close();return 0;
}///
/// @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 = td::StringConverter::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, td::StringConverter::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 = td::StringConverter::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, td::StringConverter::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(td::StringConverter::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(td::StringConverter::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);