What is the best way to handle versioning using JSON protocol?

前端 未结 4 1856
栀梦
栀梦 2020-12-23 16:38

I am normally writing all parts of the code in C# and when writing protocols that are serialized I use FastSerializer that serializes/deserializes the classes fast and effic

相关标签:
4条回答
  • 2020-12-23 16:46

    Google's java based gson library has an excellent versioning support for json. It could prove a very handy if you are thinking going java way.

    There is nice and easy tutorial here.

    0 讨论(0)
  • 2020-12-23 16:50

    It doesn't matter what serializing protocol you use, the techniques to version APIs are generally the same.

    Generally you need:

    1. a way for the consumer to communicate to the producer the API version it accepts (though this is not always possible)
    2. a way for the producer to embed versioning information to the serialized data
    3. a backward compatible strategy to handle unknown fields

    In a web API, generally the API version that the consumer accepts is embedded in the Accept header (e.g. Accept: application/vnd.myapp-v1+json application/vnd.myapp-v2+json means the consumer can handle either version 1 and version 2 of your API) or less commonly in the URL (e.g. https://api.twitter.com/1/statuses/user_timeline.json). This is generally used for major versions (i.e. backward incompatible changes). If the server and the client does not have a matching Accept header, then the communication fails (or proceeds in best-effort basis or fallback to a default baseline protocol, depending on the nature of the application).

    The producer then generates a serialized data in one of the requested version, then embed this version info into the serialized data (e.g. as a field named version). The consumer should use the version information embedded in the data to determine how to parse the serialized data. The version information in the data should also contain minor version (i.e. for backward compatible changes), generally consumers should be able to ignore the minor version information and still process the data correctly although understanding the minor version may allow the client to make additional assumptions about how the data should be processed.

    A common strategy to handle unknown fields is like how HTML and CSS are parsed. When the consumer sees an unknown fields they should ignore it, and when the data is missing a field that the client is expecting, it should use a default value; depending on the nature of the communication, you may also want to specify some fields that are mandatory (i.e. missing fields is considered fatal error). Fields added within minor versions should always be optional field; minor version can add optional fields or change fields semantic as long as it's backward compatible, while major version can delete fields or add mandatory fields or change fields semantic in a backward incompatible manner.

    In an extensible serialization format (like JSON or XML), the data should be self-descriptive, in other words, the field names should always be stored together with the data; you should not rely on the specific data being available on specific positions.

    0 讨论(0)
  • 2020-12-23 16:56

    The key to versioning JSON is to always add new properties, and never remove or rename existing properties. This is similar to how protocol buffers handle versioning.

    For example, if you started with the following JSON:

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

    And you want to rename the "foo" property to "bar", don't just rename it. Instead, add a new property:

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

    Since you are never removing properties, clients based on older versions will continue to work. The downside of this method is that the JSON can get bloated over time, and you have to continue maintaining old properties.

    It is also important to clearly define your "edge" cases to your clients. Suppose you have an array property called "fooList". The "fooList" property could take on the following possible values: does not exist/undefined (the property is not physically present in the JSON object, or it exists and is set to "undefined"), null, empty list or a list with one or more values. It is important that clients understand how to behave, especially in the undefined/null/empty cases.

    I would also recommend reading up on how semantic versioning works. If you introduce a semantic versioning scheme to your version numbers, then backwards compatible changes can be made on a minor version boundary, while breaking changes can be made on a major version boundary (both clients and servers would have to agree on the same major version). While this isn't a property of the JSON itself, this is useful for communicating the types of changes a client should expect when the version changes.

    0 讨论(0)
  • 2020-12-23 17:12

    Don't use DataContractJsonSerializer, as the name says, the objects that are processed through this class will have to:

    a) Be marked with [DataContract] and [DataMember] attributes.

    b) Be strictly compliant with the defined "Contract" that is, nothing less and nothing more that it is defined. Any extra or missing [DataMember] will make the deserialization to throw an exception.

    If you want to be flexible enough, then use the JavaScriptSerializer if you want to go for the cheap option... or use this library:

    http://json.codeplex.com/

    This will give you enough control over your JSON serialization.

    Imagine you have an object in its early days.

    public class Customer
    { 
        public string Name;
    
        public string LastName;
    }
    

    Once serialized it will look like this:

    { Name: "John", LastName: "Doe" }

    If you change your object definition to add / remove fields. The deserialization will occur smoothly if you use, for example, JavaScriptSerializer.

    public class Customer
    { 
        public string Name;
    
        public string LastName;
    
        public int Age;
    }
    

    If yo try to de-serialize the last json to this new class, no error will be thrown. The thing is that your new fields will be set to their defaults. In this example: "Age" will be set to zero.

    You can include, in your own conventions, a field present in all your objects, that contains the version number. In this case you can tell the difference between an empty field or a version inconsistence.

    So lets say: You have your class Customer v1 serialized:

    { Version: 1, LastName: "Doe", Name: "John" }
    

    You want to deserialize into a Customer v2 instance, you will have:

    { Version: 1, LastName: "Doe", Name: "John", Age: 0}
    

    You can somehow, detect what fields in your object are somehow reliable and what's not. In this case you know that your v2 object instance is coming from a v1 object instance, so the field Age should not be trusted.

    I have in mind that you should use also a custom attribute, e.g. "MinVersion", and mark each field with the minimum supported version number, so you get something like this:

    public class Customer
    { 
        [MinVersion(1)]
        public int Version;
    
        [MinVersion(1)]
        public string Name;
    
        [MinVersion(1)]
        public string LastName;
    
        [MinVersion(2)]
        public int Age;
    }
    

    Then later you can access this meta-data and do whatever you might need with that.

    0 讨论(0)
提交回复
热议问题