使用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…或者使用这个库:
这将给你足够的控制你的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的技术通常是相同的。
一般你需要:
- 一种让消费者与生产者沟通的API版本的方式(虽然这并不总是可行的)
- 这是制作者将版本信息embedded到序列化数据中的一种方式
- 一个向后兼容的策略来处理未知的领域
在一个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)中,数据应该是自描述性的,换句话说,字段名称应该总是与数据一起存储; 您不应该依赖特定位置上的特定数据。