serialization - JSONプロトコルを使用してバージョニングを処理する最良の方法は何ですか?

Translate

私は通常、コードのすべての部分をC#で記述しており、シリアル化されたプロトコルを作成するときは、クラスを高速かつ効率的にシリアル化/逆シリアル化するFastSerializerを使用します。また、非常に使いやすく、「バージョン管理」を行うこと、つまり、シリアル化のさまざまなバージョンを処理することは非常に簡単です。私が普段使っているものはこんな感じです:

public override void DeserializeOwnedData(SerializationReader reader, object context)
{
    base.DeserializeOwnedData(reader, context);
    byte serializeVersion = reader.ReadByte(); // used to keep what version we are using

    this.CustomerNumber = reader.ReadString();
    this.HomeAddress = reader.ReadString();
    this.ZipCode = reader.ReadString();
    this.HomeCity = reader.ReadString();
    if (serializeVersion > 0)
        this.HomeAddressObj = reader.ReadUInt32();
    if (serializeVersion > 1)
        this.County = reader.ReadString();
    if (serializeVersion > 2)
        this.Muni = reader.ReadString();
    if (serializeVersion > 3)
        this._AvailableCustomers = reader.ReadList<uint>();
}

そして

public override void SerializeOwnedData(SerializationWriter writer, object context)
{            
    base.SerializeOwnedData(writer, context);
    byte serializeVersion = 4; 
    writer.Write(serializeVersion);


    writer.Write(CustomerNumber);
    writer.Write(PopulationRegistryNumber);            
    writer.Write(HomeAddress);
    writer.Write(ZipCode);
    writer.Write(HomeCity);
    if (CustomerCards == null)
        CustomerCards = new List<uint>();            
    writer.Write(CustomerCards);
    writer.Write(HomeAddressObj);

    writer.Write(County);

    // v 2
    writer.Write(Muni);

    // v 4
    if (_AvailableCustomers == null)
        _AvailableCustomers = new List<uint>();
    writer.Write(_AvailableCustomers);
}

したがって、新しいものを追加したり、必要に応じてシリアル化を完全に変更したりするのは簡単です。

ただし、ここでは関係のない理由でJSONを使用したい=)現在使用していますDataContractJsonSerializerそして私は今、上記のFastSerializerを使用しているのと同じ柔軟性を持つ方法を探しています。

したがって、問題は次のとおりです。 JSONプロトコル/シリアル化を作成し、上記のようにシリアル化を詳細に記述できるようにして、別のマシンがまだバージョンを更新していないという理由だけでシリアル化を中断しないようにするための最良の方法は何ですか?

This question and all comments follow the "Attribution Required."

すべての答え

Translate

JSONのバージョン管理の鍵は、常に新しいプロパティを追加し、既存のプロパティを削除したり名前を変更したりしないことです。これはに似ていますプロトコルバッファがバージョン管理を処理する方法.

たとえば、次のJSONで開始した場合:

{
  "version": "1.0",
  "foo": true
}

また、「foo」プロパティの名前を「bar」に変更する必要があります。名前を変更するだけではありません。代わりに、新しいプロパティを追加します。

{
  "version": "1.1",
  "foo": true,
  "bar": true
}

プロパティを削除することはないため、古いバージョンに基づくクライアントは引き続き機能します。この方法の欠点は、JSONが時間の経過とともに肥大化する可能性があり、古いプロパティを維持し続ける必要があることです。

クライアントに対して「エッジ」ケースを明確に定義することも重要です。 「fooList」という配列プロパティがあるとします。 「fooList」プロパティは、次の可能な値を取ることができます:存在しない/未定義(プロパティがJSONオブジェクトに物理的に存在しないか、存在して「未定義」に設定されている)、null、空のリスト、または1つ以上の値。特に未定義/ null /空の場合は、クライアントがどのように動作するかを理解することが重要です。

また、どのように読むことをお勧めしますセマンティックバージョニング動作します。バージョン番号にセマンティックバージョニングスキームを導入すると、マイナーバージョン境界で下位互換性のある変更を行うことができ、メジャーバージョン境界で重大な変更を行うことができます(クライアントとサーバーの両方が同じメジャーバージョンに同意する必要があります) )。これはJSON自体のプロパティではありませんが、バージョンが変更されたときにクライアントが期待する必要のある変更の種類を伝えるのに役立ちます。

ソース
Translate

GoogleのJavaベースgsonライブラリjsonの優れたバージョン管理サポートがあります。 Javaを使用することを考えている場合は、非常に便利です。

素晴らしくて簡単なチュートリアルがありますここに.

ソース
Translate

名前が示すように、DataContractJsonSerializerを使用しないでください。このクラスを介して処理されるオブジェクトは、次のことを行う必要があります。

a)[DataContract]および[DataMember]属性でマークされます。

b)定義された「契約」に厳密に準拠します。つまり、定義された「契約」に厳密に準拠します。 [DataMember]が余分にあるか欠落していると、逆シリアル化が行われ、例外がスローされます。

十分な柔軟性が必要な場合は、安価なオプションを選択する場合はJavaScriptSerializerを使用するか、次のライブラリを使用してください。

http://json.codeplex.com/

これにより、JSONシリアル化を十分に制御できます。

初期のオブジェクトがあると想像してみてください。

public class Customer
{ 
    public string Name;

    public string LastName;
}

シリアル化されると、次のようになります。

{名前: "ジョン"、姓: "Doe"}

オブジェクト定義を変更してフィールドを追加/削除した場合。たとえば、JavaScriptSerializerを使用すると、逆シリアル化がスムーズに行われます。

public class Customer
{ 
    public string Name;

    public string LastName;

    public int Age;
}

最後のjsonをこの新しいクラスに逆シリアル化しようとしても、エラーはスローされません。重要なのは、新しいフィールドがデフォルトに設定されるということです。この例では、「年齢」はゼロに設定されます。

独自の規則で、バージョン番号を含む、すべてのオブジェクトに存在するフィールドを含めることができます。この場合、空のフィールドとバージョンの不整合の違いがわかります。

つまり、次のように言います。クラスCustomerv1がシリアル化されています。

{ Version: 1, LastName: "Doe", Name: "John" }

Customer v2インスタンスにデシリアライズする場合は、次のようになります。

{ Version: 1, LastName: "Doe", Name: "John", Age: 0}

どういうわけか、オブジェクトのどのフィールドがどういうわけか信頼できるものとそうでないものを検出できます。この場合、v2オブジェクトインスタンスがv1オブジェクトインスタンスからのものであることがわかっているため、フィールドAgeは信頼されるべきではありません。

「MinVersion」などのカスタム属性も使用し、サポートされている最小バージョン番号で各フィールドをマークする必要があることを念頭に置いているため、次のようになります。

public class Customer
{ 
    [MinVersion(1)]
    public int Version;

    [MinVersion(1)]
    public string Name;

    [MinVersion(1)]
    public string LastName;

    [MinVersion(2)]
    public int Age;
}

その後、このメタデータにアクセスして、必要な操作を実行できます。

ソース
Translate

使用するシリアル化プロトコルは関係ありません。APIをバージョン管理する手法は一般的に同じです。

一般的に必要なもの:

  1. コンシューマーが受け入れるAPIバージョンをプロデューサーに通信する方法(これは常に可能であるとは限りませんが)
  2. プロデューサーがシリアル化されたデータにバージョン情報を埋め込む方法
  3. 未知のフィールドを処理するための下位互換性のある戦略

Web APIでは、通常、コンシューマーが受け入れるAPIバージョンはAcceptヘッダーに埋め込まれます(例:Accept: application/vnd.myapp-v1+json application/vnd.myapp-v2+jsonつまり、コンシューマーはAPIのバージョン1とバージョン2のいずれかを処理できるか、URLであまり一般的ではありません(例:https://api.twitter.com/1/statuses/user_timeline.json)。これは通常、メジャーバージョン(つまり、下位互換性のない変更)に使用されます。サーバーとクライアントに一致するAcceptヘッダーがない場合、通信は失敗します(または、アプリケーションの性質に応じて、ベストエフォートベースで続行するか、デフォルトのベースラインプロトコルにフォールバックします)。

次に、プロデューサーは、要求されたバージョンの1つでシリアル化されたデータを生成し、このバージョン情報をシリアル化されたデータに埋め込みます(たとえば、version)。コンシューマーは、データに埋め込まれたバージョン情報を使用して、シリアル化されたデータを解析する方法を決定する必要があります。データのバージョン情報にはマイナーバージョンも含まれている必要があります(つまり、下位互換性のある変更の場合)。通常、消費者はマイナーバージョン情報を無視してデータを正しく処理できますが、マイナーバージョンを理解すると、クライアントはデータの処理方法。

未知のフィールドを処理するための一般的な戦略は、HTMLとCSSの解析方法のようなものです。コンシューマーが不明なフィールドを見つけた場合は無視する必要があり、クライアントが期待しているフィールドがデータにない場合は、デフォルト値を使用する必要があります。通信の性質によっては、必須のフィールドを指定することもできます(つまり、フィールドが欠落していると致命的なエラーと見なされます)。マイナーバージョン内で追加されるフィールドは、常にオプションのフィールドである必要があります。マイナーバージョンでは、下位互換性がある限り、オプションフィールドを追加したり、フィールドのセマンティックを変更したりできます。メジャーバージョンでは、フィールドを削除したり、必須フィールドを追加したり、フィールドのセマンティックを下位互換性のない方法で変更したりできます。

拡張可能なシリアル化形式(JSONやXMLなど)では、データは自己記述的である必要があります。つまり、フィールド名は常にデータと一緒に保存する必要があります。特定の位置で利用可能な特定のデータに依存しないでください。

ソース
Leave a Reply
You must be logged in to post a answer.
著者について
Ted