使用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); } 

所以它很容易添加新的东西,或者完全改变序列化,如果一个select。

不过,我现在想要使用JSON的原因不相关,在这里=)我目前正在使用DataContractJsonSerializer ,我现在正在寻找一种方式具有相同的灵活性,我已经使用上面的FastSerializer。

所以问题是, 创build一个JSON协议/序列化的最好方法是什么,并能够详细的序列化如上所以,我不打破只是因为另一台机器尚未更新其版本的序列化?

JSON版本化的关键是始终添加新的属性,而不要删除或重命名现有的属性。 这与协议缓冲区如何处理版本控制相似。

例如,如果您从以下JSON开始:

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

而你想把“foo”属性重命名为“bar”,不要重命名它。 相反,添加一个新的属性:

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

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

明确定义您的客户“边缘”情况也很重要。 假设你有一个名为“fooList”的数组属性。 “fooList”属性可能具有以下可能的值:不存在/未定义(该属性不是物理存在于JSON对象中,或者存在并设置为“未定义”),null,空列表或带有一个或多个值。 客户理解如何performance是非常重要的,特别是在未定义/空/空的情况下。

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

Google的基于java的gson库对json有极好的版本支持。 如果你正在考虑用java的方式,这可能会certificate是非常方便的。

这里有很好很容易的教程。

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

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

b)严格遵守所定义的“合同”,即没有其他限制。 任何额外或缺失的[DataMember]将使反序列化引发exception。

如果你想足够灵活,那么使用JavaScriptSerializer如果你想要便宜的select…或者使用这个库:

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到这个新类,不会抛出任何错误。 问题是你的新字段将被设置为默认值。 在这个例子中:“年龄”将被设置为零。

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

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

然后,你可以访问这个元数据,并做任何你可能需要的。

使用什么序列化协议并不重要,版本API的技术通常是相同的。

一般你需要:

  1. 一种让消费者与生产者沟通的API版本的方式(虽然这并不总是可行的)
  2. 这是制作者将版本信息embedded到序列化数据中的一种方式
  3. 一个向后兼容的策略来处理未知的领域

在一个Web API中,通常消费者接受的API版本embedded在Accept头中(例如Accept: application/vnd.myapp-v1+json application/vnd.myapp-v2+json表示消费者可以处理版本1和版本2的API),或者在URL中不太常见(例如https://api.twitter.com/1/statuses/user_timeline.json )。 这通常用于主要版本(即向后不兼容的更改)。 如果服务器和客户端没有匹配的Accept头,则通信失败(或根据应用的性质以尽力而为的方式或回退到默认的基线协议​​)。

生产者然后生成一个序列化的数据在一个请求的版本,然后将这个版本信息embedded序列化的数据(例如作为一个字段命名version )。 用户应该使用数据中embedded的版本信息来确定如何parsing序列化的数据。 数据中的版本信息也应该包含次要版本(即向后兼容的变更),通常消费者应该能够忽略次要版本信息并仍然正确地处理数据,尽pipe理解次要版本可能允许客户做出关于如何处理数据。

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

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