web-dev-qa-db-ja.com

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

通常、コードのすべての部分を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プロトコル/シリアル化を作成し、上記のようにシリアル化を詳細にできるようにする最良の方法は何ですか?別のマシンがまだバージョンを更新していないからといってシリアル化を中断しないようにするには?

45
Ted

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

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

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

また、「foo」プロパティの名前を「bar」に変更したい場合は、単に名前を変更しないでください。代わりに、新しいプロパティを追加します。

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

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

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

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

33
monsur

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

素敵で簡単なチュートリアルがあります こちら

16
shashankaholic

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

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

b)定義された「契約」、つまり、それが定義されているものに厳密に準拠していること。 [DataMember]が追加または不足すると、デシリアライズが行われ、例外がスローされます。

十分な柔軟性が必要な場合は、安価なオプションを使用する場合はJavaScriptSerializerを使用するか、このライブラリを使用します。

http://json.codeplex.com/

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

あなたがその初期にオブジェクトを持っていると想像してください。

public class Customer
{ 
    public string Name;

    public string LastName;
}

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

{名前: "John"、LastName: "Doe"}

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

public class Customer
{ 
    public string Name;

    public string LastName;

    public int Age;
}

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

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

たとえば、クラスCustomer v1をシリアル化しています:

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

その後、このメタデータにアクセスして、必要に応じて何でもできます。

7
Adrian Salazar

どのシリアル化プロトコルを使用するかは問題ではありません。APIをバージョン化する手法は通常同じです。

一般的に必要なもの:

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

ウェブ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ヘッダーがない場合、通信は失敗します(または、アプリケーションの性質に応じて、ベストエフォートベースまたはデフォルトのベースラインプロトコルにフォールバックします)。

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

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

拡張可能なシリアル化形式(JSONやXMLなど)では、データは自己記述的である必要があります。つまり、フィールド名は常にデータと共に保存される必要があります。特定のポジションで利用可能な特定のデータに頼るべきではありません。

5
Lie Ryan