Sur cette page

Convertisseur de sérialisation

Comment convertir entre différents formats de sérialisation.

Cet exemple permet de convertir entre les formats JSON, CBOR, XML, QDataStream et certains formats texte simples. Il peut détecter automatiquement le format utilisé ou recevoir une instruction indiquant le format à utiliser. Tous les formats ne prennent pas en charge à la fois l’entrée et la sortie, et ils ne prennent pas en charge les mêmes types de données. L’ QDataStream et XML sont les plus riches, suivis par le CBOR, puis le JSON, et enfin les formats de texte brut. La conversion via les formats les moins performants est susceptible d’entraîner une perte de structure des données.

Sortie du terminal affichant la conversion au format JSON des données de sauvegarde du jeu, comprenant les niveaux, les PNJ et les informations sur le joueur

La classe Converter

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 la classe `Converter` gèrent une liste des convertisseurs disponibles utilisés par le programme principal, afin que celui-ci sache quels convertisseurs sont disponibles. Chaque type de convertisseur définit une instance statique qui garantit sa création et donc sa disponibilité 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()` permet de déterminer si un convertisseur peut être utilisé pour l’entrée, la sortie ou les deux. Ces fonctions permettent au programme principal d’indiquer, dans son texte d’aide, quels convertisseurs sont disponibles pour les options de 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() sert à indiquer les différentes options de ligne de commande prises en charge par les formats disponibles, lorsqu’on l’interroge à 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 fonctionnalité optionnelle est la prise en charge de clés arbitraires dans les mappages de clés vers des valeurs. La fonction loadFile() d’un convertisseur d’entrée peut utiliser ces informations pour adapter la forme sous laquelle elle présente les données qu’elle a lues, afin qu’elles soient reproduites aussi fidèlement que possible par le convertisseur de sortie, dans la mesure où ses capacités le permettent.

La fonction probeFile() sert à déterminer si un fichier correspond au format du convertisseur. Le programme principal s’en sert pour déterminer quel format utiliser lors de la lecture ou de l’écriture d’un fichier, en fonction 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()` quel sérialiseur il compte utiliser, afin que `loadFile()` puisse interroger ses `outputOptions()` pour déterminer la forme sous laquelle représenter les données chargées. Si l'appelant n'a pas encore 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 depuis la ligne de commande, telles que décrites par `loadHelp()`, qui permettent d’ajuster les détails de la représentation des données lors de leur enregistrement dans un fichier.

Les fonctions `loadFile()` et `saveFile()` peuvent toutes deux être utilisées avec un ` QIODevice` quelconque. Cela signifie qu’un `Converter` peut également être utilisé avec un socket réseau ou toute autre source de données, pour y lire ou y écrire. Dans le programme actuel, le programme principal transmet toujours un ` QFile`, accédant soit à un fichier sur le disque, soit à l’un des flux standard du processus.

Les convertisseurs disponibles

Plusieurs convertisseurs sont pris en charge, ce qui illustre comment le programme de conversion pourrait être adapté à d’autres formats, si le besoin s’en faisait sentir. Consultez le code source de chacun d’entre eux pour plus de détails. Les convertisseurs CBOR constituent un exemple relativement complet du fonctionnement des convertisseurs, que nous examinerons plus en détail ci-dessous. Ce tableau résume les convertisseurs disponibles :

Classemodeformat
CborConverterEntrée/SortieCBOR
CborDiagnosticDumperSortieDiagnostic CBOR
Convertisseur de flux de donnéesEntrée/SortieQDataStream
Débogueur de texteSortieSans perte, non standard, lisible par l'homme
JsonConverterEntrée/SortieJSON
NullConverterSortiePas de sortie
TextConverterEntrée/SortieTexte brut structuré
XmlConverterEntrée/SortieXML

Ceux qui prennent en charge l’entrée s’utilisent eux-mêmes comme convertisseur de secours de la méthode loadFile(), à l’exception des convertisseurs CBOR et QDataStream, qui utilisent leurs classes compagnons respectives de sauvegarde en sortie uniquement. Le convertisseur null peut être utilisé comme convertisseur de sortie lors de l’exécution du programme, à des fins de validation ou de 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 propose diverses options pour configurer la sortie des valeurs à virgule flottante, ainsi qu’une option « signature » permettant de déterminer s’il faut commencer la sortie par une balise CBOR servant d’en-tête de fichier, indiquant que le fichier contient des données CBOR.

Il existe également une classe `CborDiagnosticDumper` permettant de générer une sortie au format de notation diagnostique CBOR. Elle ne prend pas en charge le chargement de données. Le format de sa sortie peut être configuré à l’aide de deux options. L’une permet de choisir d’utiliser ou non le format diagnostique CBOR étendu (plus détaillé). L’autre permet de contrôler si chaque valeur CBOR apparaît sur une ligne distincte.

La notation diagnostique simple est similaire à JSON, mais pas exactement, car elle permet d’afficher le contenu d’un flux CBOR sans perte, tandis qu’une conversion en JSON peut entraîner des pertes. La méthode loadFile() de CborConverter utilise CborDiagnosticDumper comme convertisseur de sortie de secours si l’appelant n’a pas lui-même déterminé le format de sortie.

Les fonctions auxiliaires `convertCborValue()`, `convertCborMap()` et `convertCborArray()` sont utilisées pour convertir une ` QCborValue ` en ` QVariant`, au profit de la méthode `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 une ` QVariant ` en ` QCborValue ` en vue de sa sortie par la méthode ` saveFile() ` de l’une ou l’autre de ces classes.

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() ` configure un ` QApplication ` et une ` QCommandLineParser ` afin d’interpréter les options spécifiées par l’utilisateur et de lui fournir une aide s’il en fait la 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 correspondant aux 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, le cas échéant, déterminer le convertisseur de sortie à utiliser, s’il n’en a pas encore sélectionné un) et son convertisseur de sortie pour sérialiser ces données, en tenant compte des options de sortie fournies par l’utilisateur 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;

Exemple de projet sur code.qt.io

Voir également «Analyse et affichage des données CBOR », « Enregistrement et chargement d'une partie » et « Prise en charge de CBOR dans 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.