serialization - Jaki jest najlepszy sposób obsługi wersji przy użyciu protokołu JSON?

Translate

Zwykle piszę wszystkie części kodu w C #, a podczas pisania protokołów, które są serializowane, używam FastSerializer, który serializuje / deserializuje klasy szybko i wydajnie. Jest również bardzo łatwy w użyciu i dość prosty do "wersjonowania", tj. Do obsługi różnych wersji serializacji. To, czego zwykle używam, wygląda następująco:

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

Dlatego łatwo jest dodawać nowe rzeczy lub całkowicie zmieniać serializację, jeśli ktoś zdecyduje.

Jednak teraz chcę używać JSON z powodów nieistotnych tutaj =) Obecnie używamDataContractJsonSerializeri teraz szukam sposobu, aby mieć taką samą elastyczność, jaką mam, używając powyższego FastSerializer.

Więc pytanie brzmi; jaki jest najlepszy sposób na utworzenie protokołu / serializacji JSON i możliwość szczegółowego opisania serializacji jak powyżej, aby nie przerywać serializacji tylko dlatego, że inna maszyna nie zaktualizowała jeszcze swojej wersji?

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

Wszystkie odpowiedzi

Translate

Kluczem do przechowywania wersji JSON jest zawsze dodawanie nowych właściwości i nigdy nie usuwanie istniejących właściwości ani ich zmiana. To jest podobne dojak bufory protokołów obsługują wersjonowanie.

Na przykład, jeśli zacząłeś od następującego kodu JSON:

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

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

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

Ponieważ nigdy nie usuwasz właściwości, klienci korzystający ze starszych wersji będą nadal działać. Wadą tej metody jest to, że JSON może z czasem ulec rozdęciu i musisz nadal utrzymywać stare właściwości.

Ważne jest również, aby jasno zdefiniować swoim klientom przypadki „skrajne”. 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 / nieokreślona (właściwość nie istnieje fizycznie w obiekcie JSON lub istnieje i jest ustawiona na wartość „niezdefiniowana”), null, pusta lista lub lista z co najmniej jedna wartość. Ważne jest, aby klienci rozumieli, jak się zachować, zwłaszcza w przypadku niezdefiniowanych / pustych / pustych.

Poleciłbym również przeczytać, jak to zrobićwersjonowanie semantycznePracuje. Jeśli wprowadzisz semantyczny schemat wersjonowania do numerów wersji, wówczas zmiany kompatybilne wstecz mogą być dokonywane na granicy wersji pomocniczej, podczas gdy zmiany przełamujące mogą być dokonywane na granicy wersji głównej (zarówno klienci, jak i serwery musieliby uzgodnić tę samą wersję główną ). Chociaż nie jest to właściwość samego JSON, jest to przydatne do komunikowania typów zmian, których klient powinien oczekiwać po zmianie wersji.

Źródło
Translate

Oparta na Javie GoogleBiblioteka gsonma doskonałą obsługę wersji dla json. Może się to okazać bardzo przydatne, jeśli myślisz o przejściu na język Java.

Jest ładny i łatwy tutorialtutaj.

Źródło
Translate

Nie używaj DataContractJsonSerializer, jak sama nazwa wskazuje, obiekty przetwarzane przez tę klasę będą musiały:

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

b) Przestrzegać ściśle zdefiniowanego „Kontraktu”, to znaczy nic mniej i nic poza tym, co zostało określone. Każdy dodatkowy lub brakujący element [DataMember] spowoduje deserializację w celu zgłoszenia wyjątku.

Jeśli chcesz być wystarczająco elastyczny, użyj JavaScriptSerializer, jeśli chcesz wybrać tanią opcję ... lub skorzystaj z tej biblioteki:

http://json.codeplex.com/

Zapewni to wystarczającą kontrolę nad serializacją JSON.

Wyobraź sobie, że masz obiekt na początku.

public class Customer
{ 
    public string Name;

    public string LastName;
}

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

{Imię: „Jan”, Nazwisko: „Doe”}

Jeśli zmienisz definicję obiektu, aby dodać / usunąć pola. Deserializacja przebiegnie bezproblemowo, jeśli użyjesz na przykład JavaScriptSerializer.

public class Customer
{ 
    public string Name;

    public string LastName;

    public int Age;
}

Jeśli spróbujesz deserializować ostatni plik JSON do tej nowej klasy, nie zostanie zgłoszony żaden błąd. Chodzi o to, że nowe pola zostaną ustawione na wartości domyślne. W tym przykładzie: „Wiek” zostanie ustawiony na zero.

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

Powiedzmy więc: Twoja klasa Customer v1 jest serializowana:

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

Chcesz przeprowadzić deserializację do instancji Customer v2, będziesz mieć:

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

Możesz jakoś wykryć, które pola w twoim obiekcie są w jakiś sposób wiarygodne, a które nie. W takim przypadku wiesz, że instancja obiektu v2 pochodzi z instancji obiektu v1, więc pole Age nie powinno być zaufane.

Mam na myśli, że powinieneś użyć również niestandardowego atrybutu, np. „MinVersion” i oznaczyć każde pole minimalnym obsługiwanym numerem wersji, dzięki czemu otrzymujesz 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żesz uzyskać dostęp do tych metadanych i zrobić z nimi wszystko, czego potrzebujesz.

Źródło
Translate

Nie ma znaczenia, jakiego protokołu serializacji używasz, techniki tworzenia wersji interfejsów API są zasadniczo takie same.

Ogólnie potrzebujesz:

  1. sposób, aby konsument mógł przekazać producentowi wersję API, którą akceptuje (choć nie zawsze jest to możliwe)
  2. sposób dla producenta na osadzenie informacji o wersji w serializowanych danych
  3. strategia kompatybilna wstecz do obsługi nieznanych pól

W internetowym interfejsie API zazwyczaj wersja interfejsu API, którą akceptuje konsument, jest osadzona w nagłówku Accept (npAccept: application/vnd.myapp-v1+json application/vnd.myapp-v2+jsonoznacza, że konsument może obsłużyć wersję 1 i 2 twojego API) lub rzadziej w adresie URL (nphttps://api.twitter.com/1/statuses/user_timeline.json). Jest to zwykle używane w przypadku wersji głównych (tj. Zmian niekompatybilnych wstecz). Jeśli serwer i klient nie mają zgodnego nagłówka Accept, komunikacja kończy się niepowodzeniem (lub przebiega zgodnie z najlepszą próbą lub wraca do domyślnego protokołu bazowego, w zależności od charakteru aplikacji).

Producent następnie generuje serializowane dane w jednej z żądanych wersji, a następnie osadza informacje o tej wersji w serializowanych danych (np. Jako pole o nazwieversion). Konsument powinien skorzystać z informacji o wersji osadzonych w danych, aby określić sposób analizowania serializowanych danych. Informacje o wersji w danych powinny również zawierać wersję pomocniczą (tj. W przypadku zmian kompatybilnych wstecz), generalnie konsumenci powinni być w stanie zignorować informacje o wersji pomocniczej i nadal poprawnie przetwarzać dane, chociaż zrozumienie wersji pomocniczej może pozwolić klientowi na przyjęcie dodatkowych założeń dotyczących sposób przetwarzania danych.

Typową strategią obsługi nieznanych pól jest sposób analizowania kodu HTML i CSS. Gdy konsument widzi nieznane pola, powinien je zignorować, a gdy w danych brakuje pola, którego oczekuje klient, 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ą traktowane jako błąd krytyczny). Pola dodane w wersjach pomocniczych powinny zawsze być polami opcjonalnymi; Wersja pomocnicza może dodawać pola opcjonalne lub zmieniać semantyczne pola, o ile jest kompatybilna wstecz, podczas gdy wersja główna może usuwać pola lub dodawać pola obowiązkowe lub zmieniać semantyczne pola w sposób niekompatybilny wstecz.

W rozszerzalnym formacie serializacji (takim jak JSON lub XML) dane powinny być samoopisowe, innymi słowy, nazwy pól powinny być zawsze przechowywane razem z danymi; nie należy polegać na dostępnych danych dotyczących konkretnych stanowisk.

Źródło
Leave a Reply
You must be logged in to post a answer.
O autorze
Ted