Convertisseur de sérialisation
Comment convertir entre différents formats de sérialisation.
Cet exemple convertit entre JSON, CBOR, XML, QDataStream et certains formats de texte simples. Il peut détecter automatiquement le format utilisé ou se faire indiquer le format à utiliser. Tous les formats ne prennent pas en charge à la fois l'entrée et la sortie, et ils ont des ensembles différents de types de données de contenu qu'ils prennent en charge. QDataStream et XML sont les plus riches, suivis par CBOR, puis JSON, et enfin les formats de texte simple. La conversion via les formats les moins performants est susceptible d'entraîner une perte de structure des données.

La classe des convertisseurs
La classe Converter est la superclasse abstraite de tous les convertisseurs vers et depuis tous les formats. Ils convertissent tous depuis ou vers la classe QVariant, qui est utilisée pour représenter toutes les structures de données en interne.
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)
Le constructeur et le destructeur de convertisseurs gèrent une liste de convertisseurs disponibles utilisée par le programme principal afin qu'il sache quels convertisseurs sont disponibles. Chaque type de convertisseur définit une instance statique qui garantit qu'il est construit et donc disponible pour le programme principal via cette liste. La méthode allConverters() permet au code de main() d'accéder à cette liste.
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(); }
La fonction name() renvoie le nom du convertisseur. La fonction directions() est utilisée pour déterminer si un convertisseur peut être utilisé pour l'entrée, la sortie ou les deux. Ces fonctions permettent au programme principal de signaler les convertisseurs disponibles dans son texte d'aide pour les options de la ligne de commande permettant de sélectionner les formats d'entrée et de sortie.
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; }
La fonction optionsHelp() est utilisée pour indiquer les différentes options de ligne de commande prises en charge par les formats disponibles, lorsqu'elle est interrogée à l'aide de l'option de ligne de commande --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 ; } }
La fonction outputOptions() indique les capacités de sortie d'un convertisseur. À l'heure actuelle, la seule caractéristique optionnelle est la prise en charge de clés arbitraires dans les correspondances entre clés et valeurs. La fonction loadFile() d'un convertisseur d'entrée peut utiliser cette information pour adapter la forme dans laquelle elle présente les données qu'elle a lues, afin que le convertisseur de sortie les représente aussi fidèlement que ses capacités le permettent.
La fonction probeFile() est utilisée pour déterminer si un fichier correspond au format du convertisseur. Le programme principal l'utilise pour déterminer le format à utiliser lors de la lecture ou de l'écriture d'un fichier, sur la base de son nom et éventuellement de son contenu, lorsque l'utilisateur n'a pas spécifié le format à utiliser sur la ligne de commande.
La fonction loadFile() désérialise les données. L'appelant indique à loadFile() le sérialiseur qu'il a l'intention d'utiliser, afin que loadFile() puisse interroger son outputOptions() pour déterminer la forme sous laquelle les données chargées doivent être représentées. Si l'appelant n'a pas choisi de convertisseur de sortie, loadFile() lui fournit un convertisseur de sortie par défaut adapté aux données qu'il renvoie.
La fonction saveFile() sérialise les données. Elle reçoit des options de la ligne de commande, comme décrit par loadHelp(), qui permettent d'ajuster les détails de la représentation des données lors de l'enregistrement dans un fichier.
Les fonctions loadFile() et saveFile() peuvent être utilisées avec un QIODevice arbitraire. Cela signifie qu'un convertisseur peut également être utilisé avec une prise réseau ou une autre source de données, pour lire ou écrire. Dans le présent programme, le programme principal passe toujours par QFile, qui accède soit à un fichier sur disque, soit à l'un des flux standard du processus.
Les convertisseurs disponibles
Plusieurs convertisseurs sont pris en charge, illustrant la manière dont le programme de conversion peut être adapté à d'autres formats, si le besoin s'en fait sentir. Voir le code source de chaque convertisseur pour plus de détails. Les convertisseurs CBOR servent d'illustration relativement complète de la façon dont les convertisseurs peuvent fonctionner, que nous examinerons plus en détail ci-dessous. Ce tableau résume les convertisseurs disponibles :
| Classe | mode | format |
|---|---|---|
| Convertisseur Cbor | Entrée/Sortie | CBOR |
| CborDiagnosticDumper | Sortie | Diagnostic CBOR |
| Convertisseur de flux de données | Entrée/Sortie | QDataStream |
| DebugTextDumper | Sortie | Sans perte, non standard, lisible par l'homme |
| Convertisseur Json | Entrée/sortie | JSON |
| Convertisseur Null | Sortie | Pas de sortie |
| Convertisseur de texte | Entrée/sortie | Texte brut structuré |
| Convertisseur Xml | Entrée/sortie | XML |
Ceux qui supportent l'entrée s'utilisent eux-mêmes comme convertisseur de repli de loadFile(), à l'exception des convertisseurs CBOR et QDataStream, qui utilisent leurs classes compagnons respectives de dumper de sortie uniquement. Le convertisseur null peut être utilisé comme convertisseur de sortie lors de l'exécution du programme afin de permettre la validation ou la vérification qu'un convertisseur d'entrée pourrait effectuer.
Les classes CborConverter et CborDiagnosticDumper
La classe CborConverter prend en charge la sérialisation vers et depuis le format CBOR. Elle prend en charge diverses options pour configurer la sortie des valeurs à virgule flottante et une option signature pour déterminer si elle doit commencer sa sortie par une balise CBOR qui sert d'en-tête de fichier, identifiant le fichier comme contenant des données CBOR.
Il existe également une classe CborDiagnosticDumper pour produire des données en notation de diagnostic CBOR. Elle ne prend pas en charge le chargement des données. La forme de sa sortie peut être configurée à l'aide de deux options. L'une d'elles permet de choisir d'utiliser ou non le format de diagnostic CBOR étendu (plus verbeux). L'autre permet de contrôler si chaque valeur CBOR apparaît sur une ligne séparée.
La notation de diagnostic simple est similaire à JSON, mais pas exactement, car elle permet d'afficher le contenu d'un flux CBOR sans perte, alors qu'une conversion vers JSON peut entraîner des pertes. La fonction loadFile() de CborConverter utilise CborDiagnosticDumper comme convertisseur de sortie de secours, si l'appelant n'a pas déterminé lui-même le format de sortie.
Les fonctions d'aide convertCborValue(), convertCborMap() et convertCborArray() sont utilisées pour convertir un QCborValue en QVariant, au profit de 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(); }
La fonction convertFromVariant() est utilisée pour convertir un QVariant en QCborValue pour la sortie par le saveFile() de l'une ou l'autre classe.
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); }
Le programme convert
La fonction main() met en place un QApplication et un QCommandLineParser pour donner un sens aux options spécifiées par l'utilisateur et fournir de l'aide si l'utilisateur le demande. Elle utilise les valeurs obtenues pour les différentes instances de QCommandLineOption décrivant les choix de l'utilisateur, ainsi que les arguments de position pour les noms de fichiers, pour préparer les convertisseurs qu'elle utilisera.
Il utilise ensuite son convertisseur d'entrée pour charger les données (et éventuellement résoudre son choix de convertisseur de sortie, s'il n'en a pas encore sélectionné un) et son convertisseur de sortie pour sérialiser ces données, en tenant compte de toutes les options de sortie que l'utilisateur a fournies sur la ligne de commande.
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;
Voir aussi Parsing and displaying CBOR data, Saving and Loading a Game, et CBOR Support in Qt.
© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.