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

Ted*_*Ted 45 versioning binary serialization 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>();
}
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)

所以很容易添加新东西,或者如果选择的话,完全改变序列化.

但是,我现在想要使用JSON,因为这里不相关的原因=)我目前正在使用DataContractJsonSerializer,我现在正在寻找一种方法来获得我使用上面的FastSerializer所具有的相同的灵活性.

所以问题是; 什么是创建JSON协议/序列化的最佳方法,并能够详细说明上面的序列化,以便我不会因为另一台机器尚未更新其版本而中断序列化?

mon*_*sur 35

版本化JSON的关键是始终添加新属性,并且永远不会删除或重命名现有属性.这类似于协议缓冲区处理版本控制的方式.

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

{
  "version": "1.0",
  "foo": true
}
Run Code Online (Sandbox Code Playgroud)

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

{
  "version": "1.1",
  "foo": true,
  "bar": true
}
Run Code Online (Sandbox Code Playgroud)

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

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

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


sha*_*lic 16

谷歌的基于Java的gson库对json有很好的版本支持.如果您正在考虑使用java方式,它可能会非常方便.

有好的,易于教程这里.


Adr*_*zar 7

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

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

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

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

http://json.codeplex.com/

这将为您提供对JSON序列化的足够控制.

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

public class Customer
{ 
    public string Name;

    public string LastName;
}
Run Code Online (Sandbox Code Playgroud)

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

{姓名:"John",姓氏:"Doe"}

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

public class Customer
{ 
    public string Name;

    public string LastName;

    public int Age;
}
Run Code Online (Sandbox Code Playgroud)

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

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

所以我们说:你的班级客户v1被序列化了:

{ Version: 1, LastName: "Doe", Name: "John" }
Run Code Online (Sandbox Code Playgroud)

您想要反序列化为Customer v2实例,您将拥有:

{ Version: 1, LastName: "Doe", Name: "John", Age: 0}
Run Code Online (Sandbox Code Playgroud)

你可以以某种方式,检测对象中的哪些字段在某种程度上是可靠的,哪些不是.在这种情况下,您知道您的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;
}
Run Code Online (Sandbox Code Playgroud)

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


Lie*_*yan 6

使用什么序列化协议并不重要,版本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标头,则通信失败(或以尽力而为的方式进行或回退到默认基线协议,具体取决于应用程序的性质).

然后,生产者在所请求的版本之一中生成序列化数据,然后将该版本信息嵌入到序列化数据中(例如,作为名为的字段version).消费者应该使用数据中嵌入的版本信息来确定如何解析序列化数据.数据中的版本信息还应包含次要版本(即,对于向后兼容的更改),通常消费者应该能够忽略次要版本信息并仍然正确处理数据,尽管理解次要版本可能允许客户做出关于其他假设的更多假设.如何处理数据.

处理未知字段的常用策略就是如何解析HTML和CSS.当消费者看到未知字段时,他们应该忽略它,当数据缺少客户期望的字段时,它应该使用默认值; 根据通信的性质,您可能还需要指定一些必需的字段(即缺少的字段被视为致命错误).在次要版本中添加的字段应始终为可选字段; 次要版本可以添加可选字段或更改字段语义,只要它向后兼容,而主要版本可以删除字段或添加必填字段或以向后不兼容的方式更改字段语义.

在可扩展的序列化格式(如JSON或XML)中,数据应该是自描述的,换句话说,字段名称应始终与数据一起存储; 您不应该依赖特定职位的具体数据.