2012-04-06 9 views
40

Zazwyczaj piszę wszystkie części kodu w języku C# i podczas pisania protokołów, które są serializowane Używam FastSerializer, który serializuje/deserializuje klas szybko i wydajnie. Jest także bardzo łatwy w użyciu i dość prosty do wykonania "wersjonowania", tj. Do obsługi różnych wersji serializacji. To, co normalnie używać, wygląda następująco:Jaki jest najlepszy sposób obsługi wersji przy użyciu protokołu JSON?

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

i

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

Więc jego łatwe dodawanie nowych rzeczy, albo zmienić serializacji całkowicie jeśli ktoś zdecyduje się.

Jednak teraz chcę używać JSON z przyczyn nie odpowiednich tutaj =) Obecnie używam DataContractJsonSerializer i jestem teraz szuka sposobu, aby mieć taką samą elastyczność Mam używając FastSerializer powyżej.

Pytanie brzmi; jaki jest najlepszy sposób tworzenia protokołu/serializacji JSON i aby móc szczegółowo serializacji jak powyżej, tak, że nie przerwać serializacji tylko dlatego, że inny komputer nie ma jeszcze zaktualizowane ich wersji?

Odpowiedz

14

Aplikacja Google java na podstawie gson library ma doskonałą obsługę wersji dla json. Może okazać się bardzo przydatny, jeśli myślisz o pójściu w kierunku java.

Jest ładny i łatwy tutorial here.

+1

Piszę w języku C#, ale implementacja powinna być możliwa w dowolnym języku, w przeciwnym razie pomija cały punkt ... – Ted

+1

+1 za dobrą wskazówkę! – Ted

6

Nie używaj DataContractJsonSerializer, jak sama nazwa mówi, że obiekty, które są przetwarzane przez tej klasy będą musiały:

a) być oznaczone [DataContract] i [] DataMember atrybuty.

b) Bądź ściśle zgodny ze zdefiniowanym "Kontraktem", który jest niczym innym jak zdefiniowanym. Każdy dodatkowy lub brakujący element [DataMember] spowoduje, że deserializacja wygeneruje wyjątek.

Jeśli chcesz być na tyle elastyczny, a następnie użyć JavaScriptSerializer jeśli chcesz iść na tanią opcją ... lub korzystać z tej biblioteki:

http://json.codeplex.com/

To daje wystarczającą kontrolę nad Serializacja JSON.

Wyobraź sobie, że masz obiekt we wczesnych latach.

public class Customer 
{ 
    public string Name; 

    public string LastName; 
} 

Po odcinkach będzie wyglądać następująco:

{name: "John", nazwisko: "Doe"}

Jeśli zmienisz definicję obiektu do dodawania/usuwania pól. Deserializacja nastąpi płynnie, jeśli użyjesz na przykład JavaScriptSerializer.

public class Customer 
{ 
    public string Name; 

    public string LastName; 

    public int Age; 
} 

Jeśli spróbujesz odserializować ostatni json do tej nowej klasy, błąd nie zostanie zgłoszony. Chodzi o to, że twoje nowe pola zostaną ustawione na domyślne. W tym przykładzie: "Wiek" zostanie ustawiony na zero.

Możesz dołączyć, we własnych konwencjach, pole obecne we wszystkich obiektach, które zawiera numer wersji. W tym przypadku możesz odróżnić puste pole lub niespójność wersji.

Więc pozwala powiedzieć: masz swoją klasę v1 Klienta odcinkach:

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

Chcesz deserializowania na przykład v2 klienta, trzeba będzie:

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

Można jakoś wykryć co pola w twoim obiekcie są w jakiś sposób niezawodne, a co nie. W tym przypadku wiesz, że twoja instancja obiektu v2 pochodzi z instancji obiektu v1, więc pole Wiek nie powinno być zaufane.

Mam na uwadze, że należy również użyć niestandardowego atrybutu, np. „MinVersion” i zaznaczyć każde pole z numerem wersji minimalnej obsługiwana, więc masz coś takiego:

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

    [MinVersion(1)] 
    public string Name; 

    [MinVersion(1)] 
    public string LastName; 

    [MinVersion(2)] 
    public int Age; 
} 

Później można uzyskać dostęp do tej meta-danych i robić, co może trzeba się z tym.

+0

Dzięki za cynk. Jednak nie widzę tam żadnej konkretnej wersji, z wyjątkiem bardzo rozdętego podejścia "ShouldSerialize". Czy o tym myślisz? – Ted

+0

@Ted, Mówię tylko, że (a) te serializery są wystarczająco elastyczne, aby mogły obsłużyć dowolne dane wejściowe. (b) jeśli masz taką elastyczność, zajmowanie się wersjonowaniem jest mniej krytyczne. Sprawdź moją edycję. –

+0

Problem polega na tym, że Atrybuty na C# są w porządku, ale jest to czytane przez implementację JAVA w systemie Android, gdzie nie mają atrybutów. – Ted

28

Kluczem do wersji JSON jest zawsze dodawać nowe właściwości i nigdy nie usuwać ani nie zmieniać istniejących właściwości. Jest to podobne do how protocol buffers handle versioning.

Na przykład, jeśli zaczęło się następujący JSON:

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

I chcesz zmienić nazwę „foo” właściwość „bar”, nie tylko zmienić jego nazwę. Zamiast tego dodaj nową właściwość:

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

Ponieważ nigdy nie usuniesz właściwości, klienci w oparciu o starsze wersje będą nadal działać. Wadą tej metody jest to, że JSON może się z czasem rozdęć, a ty musisz kontynuować utrzymywanie starych właściwości.

Ważne jest również jasne określenie swoich "krawędziowych" przypadków dla swoich klientów. Załóżmy, że masz właściwość tablicy o nazwie "fooList". Właściwość "fooList" może przyjmować następujące możliwe wartości: nie istnieje/nie jest zdefiniowana (właściwość nie jest fizycznie obecna w obiekcie JSON lub istnieje i jest ustawiona na "niezdefiniowaną"), pusta lista lub lista z jedna lub więcej wartości. Ważne jest, aby klienci zrozumieli, jak się zachować, zwłaszcza w przypadkach niezdefiniowanych/pustych/pustych.

Polecam również przeczytać, jak działa semantic versioning. Jeśli wprowadzisz semantyczny schemat wersjonowania do swoich numerów wersji, możesz dokonać zmian kompatybilnych wstecz na marginalnej granicy wersji, a zmiany łamania można wprowadzić na głównej granicy wersji (zarówno klienci, jak i serwery musieliby się zgodzić na tę samą główną wersję). Chociaż nie jest to właściwością samego JSON, jest to użyteczne do komunikowania typów zmian, których klient powinien oczekiwać po zmianie wersji.

+0

, więc czy zabrania się usuwania właściwości w węźle JSON? co się stanie, jeśli moja klasa usunie jakąś zmienną? –

+0

JSON nie zabrania usuwania właściwości. ALE jeśli klient zużywa JSON, a nieruchomość nagle znika, klient może się zepsuć. Celem strategii wersjonowania jest umożliwienie rozwoju interfejsu API przy jednoczesnym zachowaniu stabilności klientów. – monsur

3

Nie ma znaczenia, z jakiego protokołu używasz serializacji, techniki interfejsu API wersji są zasadniczo takie same.

Generalnie trzeba:

  1. sposób, aby konsument przekazuje producentowi wersję API to akceptuje (choć nie zawsze jest to możliwe)
  2. sposób dla producenta osadzić wersjonowania informacji dane serializowane
  3. kompatybilny wstecz strategia obsługiwać nieznanych pól

W API internetowej, na ogół wersję API że konsument akceptuje jest osadzony w nagłówku Accept (np. Accept: application/vnd.myapp-v1+json application/vnd.myapp-v2+json oznacza, że ​​konsument może obsługiwać wersję 1 i 2 interfejsu API) lub rzadziej w adresie URL (np. https://api.twitter.com/1/statuses/user_timeline.json). Jest to zwykle używane w przypadku głównych wersji (tj. Niekompatybilnych z wcześniejszymi zmianami). Jeśli serwer i klient nie mają pasującego nagłówka Accept, komunikacja nie powiedzie się (lub będzie kontynuowana w trybie najlepszego wysiłku lub awaryjnego do domyślnego protokołu bazowego, w zależności od charakteru aplikacji).

Następnie producent generuje zserializowane dane w jednej z żądanych wersji, a następnie osadza tę informację o wersji w danych zsekularyzowanych (np. Jako pole o nazwie version). Konsument powinien użyć informacji o wersji osadzonych w danych, aby określić sposób analizowania zserializowanych danych. Informacje o wersji w danych powinny również zawierać wersję drugorzędną (tj. Dla zmian zgodnych z wcześniejszymi wersjami), zasadniczo konsumenci powinni mieć możliwość zignorowania informacji o mniejszej wersji i nadal przetwarzać dane poprawnie, chociaż zrozumienie wersji dodatkowej może pozwolić klientowi na dodatkowe założenia dotyczące w jaki sposób dane powinny być przetwarzane.

Powszechną strategią obsługi nieznanych pól jest sposób analizowania HTML i CSS. Gdy konsument widzi nieznane pola, powinien je zignorować, a gdy brakuje danych w polu oczekiwanym przez klienta, powinien użyć wartości domyślnej; w zależności od charakteru komunikacji, możesz również chcieć określić niektóre pola, które są obowiązkowe (tj. brakujące pola są uznawane za błąd krytyczny). Pola dodane w mniejszych wersjach zawsze powinny być polem opcjonalnym; wersja minor może dodawać opcjonalne pola lub zmieniać pola semantycznie, o ile jest kompatybilna wstecz, podczas gdy wersja główna może usuwać pola lub dodawać pola obowiązkowe lub zmieniać pola semantyczne w sposób niekompatybilny wstecz.

W rozszerzalnym formacie serializacji (takich jak JSON lub XML), dane powinny być samoopisowe, innymi słowy, nazwy pól powinny być zawsze przechowywane razem z danymi; nie powinieneś polegać na konkretnych danych dostępnych na konkretnych pozycjach.