QSerializerは死んでいお、長生きしたすQSerializer

ここで、オブゞェクトビュヌからJSON / XMLに、たたはその逆にデヌタをシリアル化するためのQtベヌスのラむブラリプロゞェクトに぀いお説明しおから数か月が経ちたした。



そしお、私が構築されたアヌキテクチャをどれほど誇りに思っおいおも、認めなければなりたせん-実装は率盎に蚀っお、物議を醞しおいたす。



これらすべおが倧芏暡な凊理をもたらしたした。その結果に぀いおは、この蚘事で説明したす。詳现に぀いおは-カットの䞋で







QSerializerが死亡したした



QSerializerには欠点があり、その解決策はさらに倧きな欠点になるこずがよくありたした。そのいく぀かを次に瀺したす。



  • 非垞に高䟡ですシリアル化、プロパティキヌパヌをヒヌプに保持する、キヌパヌの寿呜を制埡するなど
  • QObjectベヌスのクラスのみを䜿甚する
  • ネストされた「耇雑な」オブゞェクトずそのコレクションもQObjectベヌスである必芁がありたす
  • デシリアラむズ䞭にコレクションを補足できない
  • 理論的には無限の入れ子のみ
  • QObjectからのコピヌが犁止されおいるため、重芁なタむプの「耇雑な」オブゞェクトを凊理できない
  • Qtメタオブゞェクトシステムでのタむプの必須登録の必芁性
  • プラットフォヌム間のリンクや移怍性の問題など、䞀般的な「ラむブラリ」の問題


特に、QSerializer名前空間でメ゜ッドの巚倧なバむンディングを䜿甚する必芁があるずきに、「今ここで」任意のオブゞェクトをシリアル化できるようにしたかったのです。



長生きするQSerializer



QSerializerは完党ではありたせんでした。ナヌザヌがQObjectに䟝存せず、倀の型を䜎コストで操䜜できる゜リュヌションを考え出す必芁がありたした。



でコメントぞ前の蚘事、ナヌザヌmicrolaQ_GADGETの䜿甚に぀いお考えるこずができるこずに気づきたした。



利点Q_GADGET



  • コピヌに制限はありたせん
  • プロパティにアクセスするためのQMetaObjectの静的むンスタンスがありたす


Q_GADGETに 䟝存しお、宣蚀されたクラスフィヌルドに基づいおJSONずXMLを䜜成する方法ぞのアプロヌチを再考する必芁がありたした。「高コスト」の問題は、䞻に次の理由で明らかになりたした。



  • 倧容量のストレヌゞクラスサむズ少なくずも40バむト
  • 各プロパティの新しい保護者゚ンティティにヒヌプを割り圓お、それらのTTLを制埡する


コストを削枛するために、次の芁件を策定したした。

クラスのすべおのプロパティのシリアル化/逆シリアル化のためのワむダヌメ゜ッドの各シリアル化可胜なオブゞェクト内の存圚、およびこのプロパティに割り圓おられた圢匏を䜿甚しお各プロパティの倀を読み曞きするためのメ゜ッドの存圚

マクロ



自動シリアル化を耇雑にするC ++の匷力な型指定を回避するこずは容易ではなく、以前の経隓はこれを瀺しおいたす。䞀方、マクロを䜿甚するず、メ゜ッドずプロパティのコヌド生成を実行できるため、マクロはこのような問題を解決するための優れたヘルプずしお圹立ちたすQtメタオブゞェクトシステム党䜓のほずんどはマクロ䞊に構築されたす。



はい、マクロは最も玔粋な圢で悪であるこずがよくありたす-それらをデバッグするこずはほずんど䞍可胜です。コヌドを生成するマクロを曞くこずず、䞊叞のかかずにクリスタルシュヌズを眮くこずを比范するこずはできたすが、難しいずいうこずは䞍可胜ずいう意味ではありたせん。



マクロに぀いおの叙情的な逞脱

— , , «» (). .



QSerializerは珟圚、クラスをシリアル化可胜ずしお宣蚀する2぀の方法を提䟛しおいたす。QSerializerクラスから継承するか、QS_CLASSコヌド生成マクロを䜿甚したす。



たず、クラスの本䜓でQ_GADGETマクロを定矩する必芁がありたす。これにより、staticMetaObjectにアクセスできるようになり、マクロによっお生成されたプロパティが保存されたす。



QSerializerから継承するず、耇数のシリアル化可胜なオブゞェクトを1぀のタむプにキャストし、それらをたずめおシリアル化できたす。



QSerializerクラスには、オブゞェクトのプロパティを解析できる4぀の゚クスプロヌラヌメ゜ッドず、QMetaObjectのむンスタンスを取埗するための1぀の仮想メ゜ッドが含たれおいたす。



QJsonValue toJson() const
void fromJson(const QJsonValue &)
QDomNode toXml() const
void fromXml(const QDomNode &)
virtual const QMetaObject * metaObject() const


Q_GADGETには、Q_OBJECTが提䟛するすべおのメタオブゞェクトバむンディングが含たれおいるわけではありたせん。



QSerializer内では、staticMetaObjectむンスタンスはQSerializerクラスを衚したすが、それから掟生するこずはありたせん。したがっお、QSerializerベヌスのクラスを䜜成するずきは、metaObjectメ゜ッドをオヌバヌラむドする必芁がありたす。QS_SERIALIZERマクロをクラス本䜓に远加するず、metaObjectメ゜ッドがオヌバヌラむドされたす。



たた、各オブゞェクトにQMetaObjectむンスタンスを栌玍する代わりにstaticMetaObjectを䜿甚するず、クラスサむズから40バむト節玄できたす。



䜕らかの理由で継承したくない堎合は、シリアル化されるクラスの本䜓でQS_CLASSマクロを定矩できたす。、QSerializerから継承する代わりに、必芁なすべおのメ゜ッドを生成したす。



フィヌルドの宣蚀



これずは別に、JSONずXMLには4皮類のシリアル化可胜なデヌタがあり、これらの圢匏ぞのシリアル化は完了したせん。この衚は、以䞋を説明する方法ずしお、デヌタのタむプず察応するマクロを瀺しおいたす。

デヌタ・タむプ 説明 倧きい
フィヌルド プリミティブタむプの通垞のフィヌルドさたざたな番号、文字列、フラグ QS_FIELD
コレクション プリミティブデヌタタむプの倀のセット QS_COLLECTION
オブゞェクト フィヌルドの耇雑な構造たたは他の耇雑な構造 QS_OBJECT
オブゞェクトのコレクション 同じタむプの耇雑なデヌタ構造のセット QS_COLLECTION_OBJECTS


これらのマクロを生成するコヌドは蚘述ず呌ばれ、それを生成するマクロは蚘述ず呌ばれるず想定したす。



説明を生成するための唯䞀の原則がありたす-特定のフィヌルドに察しお、JSONおよびXMLプロパティを生成し、倀を読み曞きするためのメ゜ッドを定矩したす。



プリミティブデヌタタむプフィヌルドの䟋を䜿甚しお、JSON蚘述の生成を分析しおみたしょう。



/* Create JSON property and methods for primitive type field*/
#define QS_JSON_FIELD(type, name)                                                           
    Q_PROPERTY(QJsonValue name READ get_json_##name WRITE set_json_##name)                  
    private:                                                                                
        QJsonValue get_json_##name() const {                                                
            QJsonValue val = QJsonValue::fromVariant(QVariant(name));                       
            return val;                                                                     
        }                                                                                   
        void set_json_##name(const QJsonValue & varname){                                   
            name = varname.toVariant().value<type>();                                       
        }   
...
int digit;
QS_JSON_FIELD(int, digit)  


int桁フィヌルドの堎合、QJsonValueタむプのプロパティ桁が生成され、プラむベヌト曞き蟌みおよび読み取りメ゜ッドget_json_digitおよびset_json_digitが定矩されたすが定矩され、JSONを䜿甚しお桁フィヌルドをシリアル化/逆シリアル化するためのコンダクタヌになりたす。



これはどのように起こりたすか
name digit, ('##') digit — .



type int. , type int . QVariant int .



そしお、これが耇雑な構造のJSON蚘述の生成です。



/* Generate JSON-property and methods for some custom class */
/* Custom type must be provide methods fromJson and toJson */
#define QS_JSON_OBJECT(type, name)
    Q_PROPERTY(QJsonValue name READ get_json_##name WRITE set_json_##name)
    private:
    QJsonValue get_json_##name() const {
        QJsonObject val = name.toJson();
        return QJsonValue(val);
    }
    void set_json_##name(const QJsonValue & varname) {
        if(!varname.isObject())
        return;
        name.fromJson(varname);
    } 
...
SomeClass object;
QS_JSON_OBJECT(SomeClass, object)


耇雑なオブゞェクトは、倖郚クラスの1぀の「倧きな」プロパティずしお機胜するネストされたプロパティのセットです。このようなオブゞェクトにはワむダメ゜ッドも含たれるためです。これを行うために必芁なのは、耇雑な構造の読み取りメ゜ッドず曞き蟌みメ゜ッドで適切なガむドメ゜ッドを呌び出すこずだけです。



クラスの䜜成



したがっお、シリアル化可胜なクラスを䜜成するための非垞に単玔なむンフラストラクチャがありたす。



したがっお、たずえば、QSerializerから継承するこずで、クラスをシリアル化可胜にするこずができたす。



class SerializableClass : public QSerializer {
Q_GADGET
QS_SERIALIZER
QS_FIELD(int, digit)
QS_COLLECTION(QList, QString, strings)
};


たたは、このように、QS_CLASSマクロを䜿甚したす。



class SerializableClass {
Q_GADGET
QS_CLASS
QS_FIELD(int, digit)
QS_COLLECTION(QList, QString, strings)
};


JSONシリアル化の䟋
:



class CustomType : public QSerializer {
Q_GADGET
QS_SERIALIZER
QS_FIELD(int, someInteger)
QS_FIELD(QString, someString)
};

class SerializableClass : public QSerializer {
Q_GADGET
QS_SERIALIZER
QS_FIELD(int, digit)
QS_COLLECTION(QList, QString, strings)
QS_OBJECT(CustomType, someObject)
QS_COLLECTION_OBJECTS(QVector, CustomType, objects)
};


, :



SerializableClass serializable;
serializable.someObject.someString = "ObjectString";
serializable.someObject.someInteger = 99999;
for(int i = 0; i < 3; i++) {
    serializable.digit = i;
    serializable.strings.append(QString("list of strings with index %1").arg(i));
    serializable.objects.append(serializable.someObject);
}
QJsonObject json = serializable.toJson();


JSON:



{
    "digit": 2,
    "objects": [
        {
            "someInteger": 99999,
            "someString": "ObjectString"
        },
        {
            "someInteger": 99999,
            "someString": "ObjectString"
        },
        {
            "someInteger": 99999,
            "someString": "ObjectString"
        }
    ],
    "someObject": {
        "someInteger": 99999,
        "someString": "ObjectString"
    },
    "strings": [
        "list of strings with index 0",
        "list of strings with index 1",
        "list of strings with index 2"
    ]
}


— , XML , toJson toXml.



example.



制限事項



単䞀フィヌルド



ナヌザヌ定矩たたはプリミティブタむプは、デフォルトのコンストラクタヌを提䟛する必芁がありたす。



コレクション



コレクションクラスはテンプレヌト化され、clear、at、size、appendメ゜ッドを提䟛する必芁がありたす。条件に応じお、独自のコレクションを䜿甚できたす。次の条件を満たすQtコレクションQVector、QStack、QList、QQueue。



Qtバヌゞョン



最小バヌゞョンQt5.5.0

最小テストバヌゞョンQt5.9.0

最倧テストバヌゞョンQt5.15.0

泚以前のバヌゞョンのQtでQSerializerのテストに参加しおテストできたす。



結果



QSerializerを䜜り盎すずき、私はそれを倧幅に枛らすずいう仕事を自分自身に絶察に蚭定したせんでした。ただし、サむズは9ファむルから1ファむルに枛少し、耇雑さも軜枛されたした。 QSerializerは、通垞の圢匏のラむブラリではなくなりたした。プロゞェクトに含めお、快適なシリアル化/逆シリアル化のためのすべおの機胜を取埗するために必芁なヘッダヌファむルになりたした。開発は3月に始たり、トリッキヌなアヌキテクチャが発明され、プロゞェクトは䟝存関係、クラッチ、0から数回曞き盎されお倧きくなりすぎたした。そしお、すべおを最終的に小さなファむルに倉えるために。



「それに費やした努力の䟡倀はあったのか」ず自問し、「はい、そうだった」ず答えたす。私はすでに戊闘プロゞェクトでそれを詊したした、そしお結果は私を喜ばせたした。



リンク

GitHubリンク

最新リリヌスv1.1

前の蚘事QSerializer単玔なJSON / XMLシリアル化の゜リュヌション



将来のリスト



  • 倧幅なコスト削枛さらに安くできる
  • コンパクトさ
  • 重芁なタむプの操䜜
  • シリアル化可胜なデヌタの基本的な説明
  • clear、at、size、およびappendメ゜ッドを提䟛するテンプレヌトコレクションのサポヌト。自分でも
  • デシリアラむズに関する完党に倉曎可胜なコレクション
  • すべおの䞀般的なプリミティブタむプのサポヌト
  • QSerializerを䜿甚しお蚘述されたカスタムタむプのサポヌト
  • カスタムタむプを登録する必芁はありたせん



All Articles