2012-04-06 36 views
40

Tôi thường viết tất cả các phần của mã trong C# và khi viết các giao thức được tuần tự hóa, tôi sử dụng FastSerializer để tuần tự hóa/deserializes các lớp nhanh và hiệu quả. Nó cũng rất dễ sử dụng, và khá thẳng về phía trước để làm "versioning", tức là để xử lý các phiên bản khác nhau của serialization. Điều tôi thường sử dụng, trông như thế này:Cách tốt nhất để xử lý phiên bản bằng giao thức JSON là gì?

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

Vì vậy, nó dễ dàng để thêm những điều mới, hoặc thay đổi serialization hoàn toàn nếu ta chọn để.

Tuy nhiên, bây giờ tôi muốn sử dụng JSON vì lý do không liên quan ngay tại đây =) Tôi hiện đang sử dụng DataContractJsonSerializer và bây giờ tôi đang tìm cách có tính linh hoạt tương tự như tôi đã sử dụng FastSerializer ở trên.

Vì vậy, câu hỏi là; cách tốt nhất để tạo ra một giao thức JSON/serialization và để có thể chi tiết serialization như trên, vì vậy mà tôi không phá vỡ serialization chỉ vì một máy khác đã không được cập nhật phiên bản của họ là gì?

Trả lời

14

java của Google dựa trên gson library có hỗ trợ phiên bản tuyệt vời cho json. Nó có thể chứng minh rất tiện dụng nếu bạn đang nghĩ đến cách java.

Có hướng dẫn đẹp và dễ dàng here.

+1

Im viết bằng C#, nhưng việc triển khai có thể thực hiện được bằng bất kỳ ngôn ngữ nào, nếu không nó sẽ bị xóa toàn bộ điểm ... – Ted

+1

+1 cho mẹo tốt! – Ted

6

Không sử dụng DataContractJsonSerializer, như tên gọi của mình, các đối tượng được xử lý qua lớp này sẽ phải:

a) Hãy đánh dấu [DataContract] và [DataMember] thuộc tính.

b) Phải tuân thủ nghiêm ngặt "Hợp đồng" được xác định, không có gì ít hơn và không có gì hơn mà nó được xác định. Bất kỳ thêm hoặc mất tích [DataMember] sẽ làm cho deserialization để ném một ngoại lệ.

Nếu bạn muốn trở thành linh hoạt đủ, sau đó sử dụng JavaScriptSerializer nếu bạn muốn đi cho tùy chọn giá rẻ ... hoặc sử dụng thư viện này:

http://json.codeplex.com/

này sẽ cung cấp cho bạn đủ quyền kiểm soát của bạn JSON serialization.

Hãy tưởng tượng bạn có một đối tượng trong những ngày đầu.

public class Customer 
{ 
    public string Name; 

    public string LastName; 
} 

Khi đăng nó sẽ giống như thế này:

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

Nếu bạn thay đổi định nghĩa đối tượng của bạn để thêm/gỡ bỏ các lĩnh vực. Việc deserialization sẽ diễn ra suôn sẻ nếu bạn sử dụng, ví dụ, JavaScriptSerializer.

public class Customer 
{ 
    public string Name; 

    public string LastName; 

    public int Age; 
} 

Nếu bạn cố gắng hủy tuần tự hóa con cuối cùng thành lớp mới này, sẽ không có lỗi nào được ném. Vấn đề là các trường mới của bạn sẽ được đặt thành mặc định của chúng. Trong ví dụ này: "Độ tuổi" sẽ được đặt thành 0.

Bạn có thể bao gồm, trong các quy ước của riêng bạn, một trường hiện diện trong tất cả các đối tượng của bạn, có chứa số phiên bản. Trong trường hợp này, bạn có thể cho biết sự khác biệt giữa một trường trống hoặc phiên bản không thống nhất.

Vì vậy, cho phép nói rằng: Bạn có lớp học của bạn v1 khách hàng serialized:

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

Bạn muốn deserialize vào một trường hợp v2 khách hàng, bạn sẽ có:

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

Bạn bằng cách nào đó có thể, phát hiện những gì các lĩnh vực trong đối tượng của bạn là bằng cách nào đó đáng tin cậy và những gì không. Trong trường hợp này, bạn biết rằng cá thể đối tượng v2 của bạn xuất phát từ một cá thể đối tượng v1, do đó trường Độ tuổi không được tin cậy.

Tôi nhớ rằng bạn cũng nên sử dụng thuộc tính tùy chỉnh, ví dụ: "MinVersion", và đánh dấu từng lĩnh vực với tối thiểu được hỗ trợ số phiên bản, vì vậy bạn sẽ có được một cái gì đó như thế này:

public class Customer 
{ 
    [MinVersion(1)] 
    public int Version; 

    [MinVersion(1)] 
    public string Name; 

    [MinVersion(1)] 
    public string LastName; 

    [MinVersion(2)] 
    public int Age; 
} 

Sau đó, sau đó bạn có thể truy cập này meta-data và làm bất cứ điều gì bạn có thể cần với điều đó.

+0

Cảm ơn mẹo. Tuy nhiên, tôi không thấy bất kỳ phiên bản cụ thể xử lý ở đó, ngoại trừ các "rất nhỏ" Shouldapperialize -approach. Đó là những gì bạn đang nghĩ đến? – Ted

+0

@Ted, tôi chỉ nói rằng (a) các serializers này đủ linh hoạt để chúng có thể xử lý bất kỳ đầu vào nào. (b) nếu bạn có sự linh hoạt như vậy, đối phó với phiên bản là ít quan trọng. Kiểm tra chỉnh sửa của tôi. –

+0

Vấn đề là thuộc tính trên C# là tốt, nhưng điều này được đọc bởi một JAVA-thực hiện trong Android, nơi họ không có thuộc tính. – Ted

28

Chìa khóa để JSON phiên bản là luôn thêm thuộc tính mới và không bao giờ xóa hoặc đổi tên thuộc tính hiện tại. Điều này tương tự như how protocol buffers handle versioning.

Ví dụ, nếu bạn bắt đầu với JSON sau:

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

Và bạn muốn đổi tên "foo" tài sản để "thanh", không chỉ đổi tên nó. Thay vào đó, hãy thêm thuộc tính mới:

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

Vì bạn không bao giờ xóa thuộc tính, khách hàng dựa trên các phiên bản cũ hơn sẽ tiếp tục hoạt động. Nhược điểm của phương thức này là JSON có thể bị cồng kềnh theo thời gian và bạn phải tiếp tục duy trì các thuộc tính cũ.

Điều quan trọng là phải xác định rõ ràng các trường hợp "cạnh" của bạn với khách hàng của bạn. Giả sử bạn có một thuộc tính mảng được gọi là "fooList". Thuộc tính "fooList" có thể nhận các giá trị sau: không tồn tại/không xác định (thuộc tính không có mặt vật lý trong đối tượng JSON, hoặc nó tồn tại và được đặt thành "không xác định"), null, danh sách trống hoặc danh sách một hoặc nhiều giá trị. Điều quan trọng là khách hàng hiểu cách cư xử, đặc biệt là trong các trường hợp không xác định/null/trống.

Tôi cũng khuyên bạn nên đọc về cách hoạt động của semantic versioning. Nếu bạn giới thiệu một lược đồ phiên bản ngữ nghĩa cho các số phiên bản của mình, thì các thay đổi tương thích ngược có thể được thực hiện trên một ranh giới phiên bản phụ.). Mặc dù đây không phải là thuộc tính của chính JSON, nhưng điều này rất hữu ích cho việc truyền đạt các loại thay đổi mà khách hàng sẽ mong đợi khi phiên bản thay đổi.

+0

vì vậy không được phép loại bỏ các thuộc tính trong nút JSON? nếu lớp của tôi loại bỏ một số biến thì sao? –

+0

JSON không cấm bạn xóa các thuộc tính. NHƯNG, nếu một khách hàng tiêu thụ JSON đó, và một tài sản đột nhiên biến mất, khách hàng đó có thể phá vỡ. Mục tiêu của chiến lược versioning là cho phép API phát triển trong khi vẫn giữ khách hàng ổn định. – monsur

3

Không quan trọng giao thức tuần tự hóa bạn sử dụng là gì, kỹ thuật đối với API phiên bản thường giống nhau.

Nói chung, bạn cần:

  1. một cách cho người tiêu dùng để giao tiếp với các nhà sản xuất phiên bản API nó chấp nhận (mặc dù điều này không phải lúc nào cũng có thể)
  2. một cách để nhà sản xuất để nhúng versioning thông tin các dữ liệu đăng
  3. một chiến lược tương thích ngược để xử lý các lĩnh vực chưa biết

Trong một API web, nói chung là các phiên bản API mà người tiêu dùng chấp nhận được nhúng trong tiêu đề Chấp nhận (ví dụ: Accept: application/vnd.myapp-v1+json application/vnd.myapp-v2+json có nghĩa là người tiêu dùng có thể xử lý phiên bản 1 và phiên bản 2 của API của bạn) hoặc ít phổ biến hơn trong URL (ví dụ: https://api.twitter.com/1/statuses/user_timeline.json). Điều này thường được sử dụng cho các phiên bản chính (tức là các thay đổi không tương thích ngược). Nếu máy chủ và máy khách không có tiêu đề Chấp nhận phù hợp, thì giao tiếp không thành công (hoặc tiến hành cơ sở tốt nhất hoặc dự phòng với giao thức cơ bản mặc định, tùy thuộc vào tính chất của ứng dụng).

Nhà sản xuất sau đó tạo dữ liệu được tuần tự hóa ở một trong các phiên bản được yêu cầu, sau đó nhúng thông tin phiên bản này vào dữ liệu được tuần tự hóa (ví dụ: trường có tên version). Người tiêu dùng nên sử dụng thông tin phiên bản được nhúng trong dữ liệu để xác định cách phân tích cú pháp dữ liệu được tuần tự hóa. Thông tin phiên bản trong dữ liệu cũng nên chứa phiên bản nhỏ (ví dụ cho các thay đổi tương thích ngược), thường người tiêu dùng có thể bỏ qua thông tin phiên bản nhỏ và vẫn xử lý dữ liệu chính xác mặc dù hiểu được phiên bản phụ có thể cho phép khách hàng đưa ra các giả định bổ sung về cách xử lý dữ liệu.

Một chiến lược chung để xử lý các trường không xác định giống như cách phân tích cú pháp HTML và CSS. Khi người tiêu dùng nhìn thấy một trường không xác định, họ nên bỏ qua nó và khi dữ liệu thiếu trường mà máy khách đang mong đợi, nó sẽ sử dụng giá trị mặc định; tùy thuộc vào bản chất của thông tin liên lạc, bạn cũng có thể muốn chỉ định một số trường bắt buộc (nghĩa là các trường bị thiếu được coi là lỗi nghiêm trọng). Các trường được thêm vào trong các phiên bản nhỏ nên luôn là trường tùy chọn; phiên bản nhỏ có thể thêm các trường tùy chọn hoặc thay đổi trường ngữ nghĩa miễn là tương thích ngược, trong khi phiên bản chính có thể xóa trường hoặc thêm trường bắt buộc hoặc thay đổi trường ngữ nghĩa theo cách không tương thích ngược.

Trong định dạng tuần tự hóa mở rộng (như JSON hoặc XML), dữ liệu phải tự mô tả, nói cách khác, tên trường phải luôn được lưu trữ cùng với dữ liệu; bạn không nên dựa vào dữ liệu cụ thể có sẵn trên các vị trí cụ thể.

Các vấn đề liên quan