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

Translate

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

This question and all comments follow the "Attribution Required."

所有的回答

Translate

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

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

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

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

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

由于您永远不会删除属性,因此基于旧版本的客户端将继续工作。这种方法的缺点是JSON会随着时间的流逝而膨胀,并且您必须继续维护旧的属性。

向客户明确定义“边缘”案例也很重要。假设您有一个名为“ fooList”的数组属性。 “ fooList”属性可以采用以下可能的值:不存在/未定义(该属性在JSON对象中实际上不存在,或者它存在并将其设置为“未定义”),空,空列表或带有一个或多个值。客户必须了解行为方式,这一点很重要,尤其是在未定义/空/空的情况下。

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

来源
Translate

谷歌基于Javagson库对json具有出色的版本支持。如果您正在考虑采用Java方式,那么可能会非常方便。

有一个很好的和简单的教程这里.

来源
Translate

请勿使用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反序列化到这个新类,则不会引发任何错误。问题是您的新字段将被设置为其默认值。在此示例中:“年龄”将设置为零。

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

因此,可以说:您已将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;
}

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

来源
Translate

不管使用什么序列化协议,版本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)中,数据应为自描述性的,换句话说,字段名称应始终与数据一起存储。您不应依赖特定职位上可用的特定数据。

来源
Leave a Reply
You must be logged in to post a answer.
关于作者
Ted