Auf dieser Seite

Serialisierungs-Konverter

So konvertieren Sie zwischen verschiedenen Serialisierungsformaten.

Dieses Beispiel konvertiert zwischen JSON, CBOR, XML, „ QDataStream “ und einigen einfachen Textformaten. Es kann das verwendete Format automatisch erkennen oder man kann ihm vorgeben, welches Format verwendet werden soll. Nicht alle Formate unterstützen sowohl die Eingabe als auch die Ausgabe, und sie unterscheiden sich hinsichtlich der unterstützten Datentypen für Inhalte. „ QDataStream “ und XML bieten den größten Umfang, gefolgt von CBOR, dann JSON und schließlich den reinen Textformaten. Bei der Konvertierung über die weniger leistungsfähigen Formate kann es leicht zu Strukturverlusten in den Daten kommen.

Terminalausgabe, die die JSON-Konvertierung von Spielspeicherdaten mit Levels, NPCs und Spielerinformationen zeigt

Die Converter-Klasse

Die Klasse „Converter“ ist die abstrakte Oberklasse für alle Konverter von und zu allen Formaten. Sie alle konvertieren von oder in die Klasse „ QVariant “, die intern zur Darstellung aller Datenstrukturen verwendet wird.

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)

Der Konstruktor und der Destruktor der „Converter“-Klasse verwalten eine Liste der verfügbaren Konverter, die vom Hauptprogramm verwendet werden, damit dieses weiß, welche Konverter zur Verfügung stehen. Jeder Konvertertyp definiert eine statische Instanz, die sicherstellt, dass er instanziiert wird und somit über diese Liste für das Hauptprogramm verfügbar ist. Die Methode „ allConverters() “ gewährt dem Code von „ main() “ Zugriff auf die 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();
}

Die Funktion `name()` gibt den Namen des Konverters zurück. Die Funktion `directions()` dient dazu, festzustellen, ob ein Konverter für die Eingabe, die Ausgabe oder beides verwendet werden kann. Dadurch kann das Hauptprogramm in seinem Hilfetext zu den Befehlszeilenoptionen zur Auswahl von Eingabe- und Ausgabeformaten angeben, welche Konverter verfügbar sind.

    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;
    }

Die Funktion `optionsHelp()` wird verwendet, um die verschiedenen Befehlszeilenoptionen aufzulisten, die von den verfügbaren Formaten unterstützt werden, wenn sie über die Befehlszeilenoption ` --format-options <format> ` abgefragt werden.

       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;
            }
        }

Die Funktion `outputOptions()` gibt Auskunft über die Ausgabefunktionen eines Konverters. Derzeit ist die einzige optionale Funktion die Unterstützung beliebiger Schlüssel bei der Zuordnung von Schlüsseln zu Werten. Die Funktion `loadFile()` eines Eingabekonverters kann diese Informationen nutzen, um die Darstellung der gelesenen Daten so anzupassen, dass sie vom Ausgabekonverter so originalgetreu wie möglich wiedergegeben werden, soweit dessen Möglichkeiten dies zulassen.

Die Funktion `probeFile()` dient dazu, festzustellen, ob eine Datei dem Format des Konverters entspricht. Das Hauptprogramm nutzt dies, um anhand des Dateinamens und gegebenenfalls des Inhalts zu bestimmen, welches Format beim Lesen oder Schreiben einer Datei verwendet werden soll, wenn der Benutzer das zu verwendende Format nicht über die Befehlszeile angegeben hat.

Die Funktion `loadFile()` deserialisiert Daten. Der Aufrufer teilt `loadFile()` mit, welchen Serializer er verwenden möchte, damit `loadFile()` dessen `outputOptions()` abfragen kann, um die Darstellungsform der geladenen Daten festzulegen. Hat sich der Aufrufer noch nicht für einen Ausgabekonverter entschieden, stellt `loadFile()` ihm einen Standard-Ausgabekonverter zur Verfügung, der für die zurückgegebenen Daten geeignet ist.

Die Funktion `saveFile()` serialisiert Daten. Ihr werden Optionen über die Befehlszeile übergeben, wie von `loadHelp()` beschrieben, mit denen die Details der Darstellung der Daten beim Speichern in eine Datei angepasst werden können.

Sowohl `loadFile()` als auch `saveFile()` können mit einem beliebigen ` QIODevice` verwendet werden. Das bedeutet, dass ein `Converter` auch mit einem Netzwerk-Socket oder einer anderen Datenquelle zum Lesen oder Schreiben verwendet werden könnte. Im vorliegenden Programm übergibt das Hauptprogramm stets einen ` QFile`, der entweder auf eine Datei auf der Festplatte oder auf einen der Standard-Streams des Prozesses zugreift.

Die verfügbaren Konverter

Es werden mehrere Konverter unterstützt, die veranschaulichen, wie das Konverterprogramm bei Bedarf an andere Formate angepasst werden könnte. Einzelheiten finden Sie im jeweiligen Quellcode. Die CBOR-Konverter dienen als relativ umfassendes Beispiel dafür, wie Konverter funktionieren können, worauf wir im Folgenden näher eingehen werden. Diese Tabelle fasst die verfügbaren Konverter zusammen:

KlasseModusFormat
CborConverterEin/AusCBOR
CborDiagnosticDumperAusCBOR-Diagnose
DataStreamConverterEin/AusQDataStream
DebugTextDumperAusVerlustfrei, nicht standardisiert, für Menschen lesbar
JsonConverterEingabe/AusgabeJSON
NullConverterAusKeine Ausgabe
TextConverterEingabe/AusgabeStrukturierter Klartext
XmlConverterEin-/AusXML

Diejenigen, die die Eingabe unterstützen, dienen selbst als Fallback-Konverter für `loadFile()`, mit Ausnahme der Konverter „CBOR“ und „ QDataStream “, die ihre jeweiligen, ausschließlich für die Ausgabe bestimmten Begleitklassen verwenden. Der Null-Konverter kann bei der Ausführung des Programms als Ausgabekonverter verwendet werden, um etwaige Validierungen oder Überprüfungen durchzuführen, die ein Eingabekonverter möglicherweise vornimmt.

Die Klassen `CborConverter` und `CborDiagnosticDumper`

Die Klasse `CborConverter` unterstützt die Serialisierung in das und aus dem CBOR-Format. Sie bietet verschiedene Optionen zur Konfiguration der Ausgabe von Gleitkommawerten sowie eine Option ` signature `, mit der festgelegt werden kann, ob die Ausgabe mit einem CBOR-Tag beginnen soll, das als Dateikopf dient und angibt, dass die Datei CBOR-Daten enthält.

Außerdem gibt es eine `CborDiagnosticDumper`-Klasse zur Ausgabe in der CBOR-Diagnosekonvention. Sie unterstützt das Laden von Daten nicht. Die Form der Ausgabe lässt sich mithilfe von zwei Optionen konfigurieren. Mit der einen Option wird festgelegt, ob das (ausführlichere) erweiterte CBOR-Diagnoseformat verwendet werden soll. Die andere Option steuert, ob jeder CBOR-Wert in einer separaten Zeile angezeigt wird.

Die einfache Diagnosenotation ähnelt JSON, ist jedoch nicht identisch, da sie die verlustfreie Darstellung des Inhalts eines CBOR-Streams unterstützt, während eine Konvertierung in JSON zu Datenverlusten führen kann. Die Methode `loadFile()` von `CborConverter` verwendet `CborDiagnosticDumper` als Ausweichkonverter für die Ausgabe, falls der Aufrufer das Ausgabeformat nicht selbst festgelegt hat.

Die Hilfsfunktionen `convertCborValue()`, `convertCborMap()` und `convertCborArray()` dienen dazu, ein ` QCborValue ` in ein ` QVariant` zu konvertieren, was für `CborConverter::loadFile()` von Nutzen ist.

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();
}

Die Funktion `convertFromVariant()` wird verwendet, um ein ` QVariant ` in ein ` QCborValue ` zu konvertieren, damit es von der Methode ` saveFile() ` einer der beiden Klassen ausgegeben werden kann.

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);
}

Das convert-Programm

Die Funktion „ main() “ richtet ein „ QApplication “ und ein „ QCommandLineParser “ ein, um die vom Benutzer angegebenen Optionen auszuwerten und auf Wunsch des Benutzers Hilfe bereitzustellen. Sie verwendet die Werte, die für die verschiedenen „ QCommandLineOption “-Instanzen zur Beschreibung der Benutzerauswahl ermittelt wurden, sowie die Positionsargumente für Dateinamen, um die zu verwendenden Konverter vorzubereiten.

Anschließend verwendet es seinen Eingabekonverter, um Daten zu laden (und gegebenenfalls die Wahl des Ausgabekonverters zu treffen, falls noch keiner ausgewählt wurde), sowie seinen Ausgabekonverter, um diese Daten zu serialisieren, wobei alle vom Benutzer über die Befehlszeile angegebenen Ausgabeoptionen berücksichtigt werden.

    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;

Beispielprojekt @ code.qt.io

Siehe auch „Parsen und Anzeigen von CBOR-Daten“, „Speichern und Laden eines Spiels“ sowie „CBOR-Unterstützung 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.