使用JSON协议处理版本控制的最佳方法是什么?

时间:2012-04-06 11:20:41

标签: json serialization binary versioning

我通常在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协议/序列化的最佳方法,并且能够如上所述详细说明序列化,以便我不会因为另一台机器尚未更新其版本而中断序列化?

3 个答案:

答案 0 :(得分:32)

版本控制JSON的关键是始终添加新属性,并且永远不会删除或重命名现有属性。这类似于how protocol buffers handle versioning

例如,如果您使用以下JSON开始:

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

并且您想将“foo”属性重命名为“bar”,不要只重命名它。而是添加一个新属性:

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

由于您永远不会删除属性,因此基于旧版本的客户端将继续工作。这种方法的缺点是随着时间的推移JSON会变得臃肿,你必须继续维护旧的属性。

向客户明确定义“边缘”案例也很重要。假设您有一个名为“fooList”的数组属性。 “fooList”属性可以采用以下可能的值:不存在/未定义(该属性实际上不存在于JSON对象中,或者存在且设置为“未定义”),null,空列表或包含一个或多个值。客户端了解如何操作非常重要,尤其是在undefined / null / empty情况下。

我还建议您阅读semantic versioning的工作原理。如果您为版本号引入语义版本控制方案,则可以在次要版本边界上进行向后兼容的更改,同时可以在主要版本边界上进行更改(客户端和服务器都必须就相同的主要版本达成一致) )。虽然这不是JSON本身的属性,但这对于传达客户端在版本更改时应该期望的更改类型非常有用。

答案 1 :(得分:16)

Google基于java的gson library对json提供了出色的版本支持。如果您正在考虑使用java方式,它可能会非常方便。

有一个很好的简单教程here

答案 2 :(得分:7)

不要使用DataContractJsonSerializer,正如名称所示,通过此类处理的对象必须:

a)标有[DataContract]和[DataMember]属性。

b)严格遵守已定义的“合同”,即合同中的任何内容,仅限定义。任何额外的或缺少的[DataMember]都会使反序列化抛出异常。

如果你想要足够灵活,那么如果你想购买便宜的选项,请使用JavaScriptSerializer ......或者使用这个库:

http://json.codeplex.com/

这将为您提供对JSON序列化的充分控制。

想象一下,你早期就有一个物体。

public class Customer
{ 
    public string Name;

    public string LastName;
}

序列化后,它将如下所示:

{姓名:“John”,姓氏:“Doe”}

如果您更改对象定义以添加/删除字段。如果您使用JavaScriptSerializer,则反序列化将顺利进行。

public class Customer
{ 
    public string Name;

    public string LastName;

    public int Age;
}

如果你试图将最后一个json反序列化为这个新类,则不会抛出任何错误。问题是您的新字段将设置为默认值。在此示例中:“Age”将设置为零。

您可以在自己的约定中包含所有对象中包含版本号的字段。在这种情况下,您可以区分空字段或版本不一致。

所以我们说:你的班级客户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;
}

然后,您可以访问此元数据,并使用它执行您可能需要的任何操作。