封装目标
- 能够将 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);