シリアライゼーション・コンバータ

異なるシリアライズ形式間の変換方法。

この例では、JSON、CBOR、XML、QDataStream 、いくつかの単純なテキスト形式を変換します。使用されているフォーマットを自動検出することも、どのフォーマットを使用するかを指定することもできます。すべてのフォーマットが入力と出力の両方をサポートしているわけではなく、どのコンテンツ・データ型をサポートしているかはそれぞれ異なる。QDataStream 、XMLが最も豊富で、CBOR、JSON、プレーンテキストフォーマットの順となる。能力の低いフォーマットを使って変換すると、データから構造が失われがちです。

コンバーター・クラス

Converterクラスは、すべてのフォーマットとのコンバーターの抽象スーパークラスです。これらのクラスはすべて、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)

コンバータのコンストラクタとデストラクタは、メイン・プログラムが使用可能なコンバータのリストを管理する。各コンバーター・タイプは静的インスタンスを定義し、それが構築されていることを保証するため、このリストを通じてメイン・プログラムから利用できるようになる。allConverters() メソッドはmain() のコードにリストへのアクセスを提供する。

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() 関数はコンバーターの名前を返す。directors()関数は、コンバーターが入力に使えるのか、出力に使えるのか、あるいは両方に使えるのかを判断するために使われる。これらにより、メイン・プログラムは、入出力形式を選択するコマンドライン・オプションのヘルプ・テキストで、どのコンバータが使用可能かを報告できるようになります。

    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
Cbor診断ダンパ出力CBOR診断
データストリームコンバーターイン/アウトQDataStream
デバッグテキストダンパアウトロスレス、非標準、人間が読める
Jsonコンバーターイン/アウトJSON
Nullコンバーター出力出力なし
テキスト・コンバーターイン/アウト構造化プレーンテキスト
Xmlコンバーター入出力XML

入力をサポートするコンバータは、それ自身を loadFile() のフォールバック・コンバータとして使用します。ただし、CBOR とQDataStream コンバータは例外で、それぞれの出力専用ダンパーのコンパニオン・クラスを使用します。NULLコンバータは、入力コンバータが行う検証や確認のためにプログラムを実行する際に出力コンバータとして使用することができる。

CborConverterクラスとCborDiagnosticDumperクラス

CborConverterクラスはCBORフォーマットとの間のシリアライズをサポートします。浮動小数点値の出力を設定するさまざまなオプションと、ファイルがCBORデータを含むことを示すファイルヘッダとして機能するCBORタグで出力を開始するかどうかを決定するsignature オプションをサポートしている。

CborDiagnosticDumperクラスもあり、CBOR診断表記で出力します。データのロードはサポートしていない。出力の形式は2つのオプションで設定できる。1つは(より冗長な)拡張CBOR診断フォーマットを使用するかどうかを選択します。もう1つは、各CBOR値を別々の行に表示するかどうかを制御する。

プレーンな診断記法はJSONに似ているが、JSONへの変換は非可逆になる可能性がある一方で、CBORストリームの内容を非可逆に表示することをサポートしているため、正確ではない。CborConverterのloadFile()は、呼び出し元が出力フォーマットを決定していない場合、フォールバック出力コンバータとしてCborDiagnosticDumperを使用する。

convertCborValue()、convertCborMap()、convertCborArray()ヘルパー関数は、CborConverter::loadFile()のために、QCborValueQVariant に変換するために使用されます。

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() 関数は、QVariantQCborValue に変換し、いずれかのクラスの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);
}

変換プログラム

main() 関数は、QApplicationQCommandLineParser をセットアップし、ユーザーが指定したオプションを理解し、ユーザーが要求した場合にヘルプを提供します。ユーザーの選択を記述したさまざまな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;

プロジェクト例 @ code.qt.io

CBORデータの解析と表示ゲームの保存と読み込みQtのCBORサポートも参照してください

©2024 The Qt Company Ltd. ここに含まれるドキュメントの著作権はそれぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。