序列化转换器
如何在不同序列化格式之间进行转换。
此示例在JSON、CBOR、XML、QDataStream和某些简单的文本格式之间进行转换。它可以自动检测正在使用的格式,或者告诉使用哪个格式。并非所有格式都支持输入和输出,它们支持的内容数据类型集也不同。《QDataStream》和XML最为丰富,其次是CBOR,然后是JSON,最后是纯文本格式。通过能力较弱的格式进行转换可能会丢失数据的结构。
转换器类
转换器类是所有转换器到和从所有格式的抽象父类。它们都从QVariant类转换,该类用于内部表示所有数据结构。
class Converter { static QList<const Converter *> &converters(); protected: Converter(); static bool isNull(const Converter *converter); // in nullconverter.cpp public: static const QList<const Converter *> &allConverters(); enum class Direction { In = 1, Out = 2, InOut = In | Out }; Q_DECLARE_FLAGS(Directions, Direction) enum Option { SupportsArbitraryMapKeys = 0x01 }; Q_DECLARE_FLAGS(Options, Option) virtual ~Converter() = 0; virtual QString name() const = 0; virtual Directions directions() const = 0; virtual Options outputOptions() const; virtual const char *optionsHelp() const; virtual bool probeFile(QIODevice *f) const; virtual QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const; virtual void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) const = 0; }; Q_DECLARE_OPERATORS_FOR_FLAGS(Converter::Directions) Q_DECLARE_OPERATORS_FOR_FLAGS(Converter::Options)
转换器构造函数和析构函数管理由主程序使用的可用转换器列表,以便它知道有什么转换器可用。每种转换器类型定义了一个静态实例,确保它被构造并且 thus 通过此列表对主程序可用。《allConverters()`方法为主程序的代码提供了访问该列表的权限。
Converter::Converter() { converters().append(this); } Converter::~Converter() { converters().removeAll(this); } QList<const Converter *> &Converter::converters() { Q_CONSTINIT static QList<const Converter *> store; return store; } const QList<const Converter *> &Converter::allConverters() { return converters(); }
name()函数返回转换器的名称。directions()函数用于确定转换器是否可用于输入、输出或两者都可用。这使得主程序能够在命令行选项的帮助文本中报告其提供的转换器,以便选择输入和输出格式。
QStringList inputFormats; QStringList outputFormats; for (const Converter *conv : Converter::allConverters()) { auto direction = conv->directions(); QString name = conv->name(); if (direction.testFlag(Converter::Direction::In)) inputFormats << name; if (direction.testFlag(Converter::Direction::Out)) outputFormats << name; }
optionsHelp()函数用于报告由可用格式支持的各个命令行选项,当使用其--format-options <format>
命令行选项进行查询时。
for (const Converter *conv : Converter::allConverters()) { if (conv->name() == format) { const char *help = conv->optionsHelp(); if (help) { qInfo("The following options are available for format '%s':\n\n%s", qPrintable(format), help); } else { qInfo("Format '%s' supports no options.", qPrintable(format)); } return EXIT_SUCCESS; } }
outputOptions()函数报告转换器的输出能力。目前唯一的可选功能是对从键到值的映射中任意键的支持。输入转换器的loadFile()可以使用此信息调整其呈现读取数据的格式,以便输出转换器以最忠实的方式表示数据。
probeFile()函数用于确定文件是否与转换器的格式匹配。主程序使用此信息来根据其名称和内容(如果有的话)确定在用户没有在命令行上指定使用的格式时使用哪种格式。
loadFile()函数反序列化数据。调用者告诉loadFile()它打算使用哪个序列器,以便loadFile()可以查询其outputOptions()以确定表示加载数据的格式。如果调用者还没有确定输出转换器选择,loadFile()会提供适合返回数据的默认输出转换器。
saveFile()函数序列化数据。它接收来自命令行的选项,如loadHelp()所述,这些选项可调整在保存到文件时表示数据的详细程度。
loadFile() 和 saveFile() 函数都可以与任意的 QIODevice 一起使用。这意味着转换器也可以与网络套接字或其他数据源一起使用,以读取或写入。在当前程序中,主程序总是传递一个 QFile,访问磁盘上的文件或进程的任何一个标准流。
可用的转换器
支持多个转换器,展示了如果需要,转换器程序如何适应其他格式。请参阅每个源代码以获取其详细信息。CBOR 转换器作为相对完整功能的示例,我们将更详细地探讨它。此表总结了可用的转换器
类 | 模式 | 格式 |
---|---|---|
CborConverter | 输入/输出 | CBOR |
CborDiagnosticDumper | 输出 | CBOR 诊断 |
DataStreamConverter | 输入/输出 | QDataStream |
DebugTextDumper | 输出 | 无损,非标准,可读性高 |
JsonConverter | 输入/输出 | JSON |
NullConverter | 输出 | 没有输出 |
TextConverter | 输入/输出 | 结构化纯文本 |
XmlConverter | 输入/输出 | XML |
支持输入的转换器将自身作为 loadFile() 的后备转换器,除 CBOR 和 QDataStream 转换器外,这些转换器使用它们各自的只输出 dumper 伴随类。null 转换器可以用作输出转换器,当运行程序进行任何验证或验证输入转换器可能执行的操作时。
CborConverter 和 CborDiagnosticDumper 类
CborConverter 类支持将数据序列化和反序列化到 CBOR 格式。它支持各种选项来配置浮点数的输出,以及一个 signature
选项来决定是否以一个作为文件开头标识文件的 CBOR 标签。
还有一个 CborDiagnosticDumper 类用于输出 CBOR 诊断表示法。它不支持加载数据。其输出的形式可以使用两个选项进行配置。一个选项用于选择是否使用(更详细的)扩展 CBOR 诊断格式。另一个选项控制是否每个 CBOR 值在单独的一行上显示。
纯诊断表示法类似于 JSON,但并不完全相同,因为它支持无损地显示 CBOR 流的内容,而转换为 JSON 可能会丢失数据。CborConverter 的 loadFile() 使用 CborDiagnosticDumper 作为后备输出转换器,如果其调用者没有确定输出格式的话。
convertCborValue()、convertCborMap() 和 convertCborArray() 辅助函数用于将 QCborValue 转换为 QVariant,以供 CborConverter::loadFile() 使用。
static QVariant convertCborValue(const QCborValue &value); static QVariant convertCborMap(const QCborMap &map) { VariantOrderedMap result; result.reserve(map.size()); for (auto pair : map) result.append({ convertCborValue(pair.first), convertCborValue(pair.second) }); return QVariant::fromValue(result); } static QVariant convertCborArray(const QCborArray &array) { QVariantList result; result.reserve(array.size()); for (auto value : array) result.append(convertCborValue(value)); return result; } static QVariant convertCborValue(const QCborValue &value) { if (value.isArray()) return convertCborArray(value.toArray()); if (value.isMap()) return convertCborMap(value.toMap()); return value.toVariant(); }
convertFromVariant() 函数用于将 variants 转换为 QCborValue 以供任一类的 saveFile() 输出。
enum TrimFloatingPoint { Double, Float, Float16 }; static QCborValue convertFromVariant(const QVariant &v, TrimFloatingPoint fpTrimming) { if (v.userType() == QMetaType::QVariantList) { const QVariantList list = v.toList(); QCborArray array; for (const QVariant &v : list) array.append(convertFromVariant(v, fpTrimming)); return array; } if (v.userType() == qMetaTypeId<VariantOrderedMap>()) { const auto m = qvariant_cast<VariantOrderedMap>(v); QCborMap map; for (const auto &pair : m) map.insert(convertFromVariant(pair.first, fpTrimming), convertFromVariant(pair.second, fpTrimming)); return map; } if (v.userType() == QMetaType::Double && fpTrimming != Double) { float f = float(v.toDouble()); if (fpTrimming == Float16) return float(qfloat16(f)); return f; } return QCborValue::fromVariant(v); }
convert 程序
main() 函数设置一个 QApplication 和一个 QCommandLineParser 来理解用户指定的选项并当用户请求帮助时提供帮助。它使用从描述用户选择的多种 QCommandLineOption 实例获取的值,以及用于文件名的位置参数,来准备它将使用的转换器。
然后它使用其输入转换器加载数据(以及可能解决其尚未选择的输出转换器,如果有的话)并使用其输出转换器序列化这些数据,考虑用户在命令行上提供的任何输出选项。
QStringList files = parser.positionalArguments(); QFile input(files.value(0)); QFile output(files.value(1)); const Converter *inconv = prepareConverter(parser.value(inputFormatOption), Converter::Direction::In, &input); const Converter *outconv = prepareConverter(parser.value(outputFormatOption), Converter::Direction::Out, &output); // Now finally perform the conversion: QVariant data = inconv->loadFile(&input, outconv); Q_ASSERT_X(outconv, "Serialization Converter", "Internal error: converter format did not provide default"); outconv->saveFile(&output, data, parser.values(optionOption)); return EXIT_SUCCESS;
另请参阅 解析和显示 CBOR 数据, 保存和加载游戏,以及 Qt 中的 CBOR 支持。
© 2024 Qt 公司有限公司。本文档中的文档贡献是各自所有者的版权。本文档提供的文档是根据自由软件基金会发布的 GNU 自由文档许可第 1.3 版 许可的。Qt 和相关徽标是芬兰和/或世界其他地区 Qt 公司的商标。所有其他商标均为其各自所有者的财产。