Каков наилучший способ обработки версий с использованием протокола JSON?
Я обычно пишу все части кода на С#, а при написании протоколов, которые сериализованы, я использую 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/сериализации и для детализации сериализации, как описано выше, так что я не нарушаю сериализацию только потому, что другой компьютер еще не обновил свою версию?
Ответы
Ответ 1
Ключ к управлению версиями JSON - это всегда добавлять новые свойства и никогда не удалять или переименовывать существующие свойства. Это похоже на как буферы протокола обрабатывают управление версиями.
Например, если вы начали со следующего JSON:
{
"version": "1.0",
"foo": true
}
И вы хотите переименовать свойство "foo" в "bar", не просто переименовать его. Вместо этого добавьте новое свойство:
{
"version": "1.1",
"foo": true,
"bar": true
}
Поскольку вы никогда не удаляете свойства, клиенты, основанные на более старых версиях, будут продолжать работать. Недостатком этого метода является то, что JSON может раздуваться с течением времени, и вы должны продолжать поддерживать старые свойства.
Также важно четко определить ваши "крайние" случаи для ваших клиентов. Предположим, что у вас есть свойство массива, называемое "fooList". Свойство "fooList" может принимать следующие возможные значения: не существует / undefined (свойство физически не присутствует в объекте JSON или оно существует и установлено в "undefined" ), нулевой, пустой список или список с одним или несколькими значениями. Важно, чтобы клиенты понимали, как себя вести, особенно в случаях undefined/null/empty.
Я бы также рекомендовал прочитать, как работает семантическое управление версиями. Если вы вводите схему версий семантической версии для своих номеров версий, то обратные совместимые изменения могут быть сделаны на границе второстепенной версии, в то время как нарушение изменений может быть выполнено на главной границе версии (оба клиента и серверы должны были бы согласовать одну и ту же основную версию). Хотя это не является свойством самого JSON, это полезно для передачи типов изменений, которые клиент должен ожидать при изменении версии.
Ответ 2
Google java gson library имеет отличную поддержку версий для json. Это может оказаться очень удобным, если вы думаете о том, чтобы перейти на Java.
Здесь есть хороший и простой учебник .
Ответ 3
Не используйте DataContractJsonSerializer, как сказано в названии, объекты, обработанные этим классом, должны будут:
a) Отметьте атрибуты [DataContract] и [DataMember].
b) Строго соблюдайте определенный "Контракт", который не меньше и не более того, что он определен. Любой дополнительный или отсутствующий [DataMember] сделает десериализацию для исключения.
Если вы хотите быть достаточно гибким, используйте JavaScriptSerializer, если хотите пойти по дешевой опции... или использовать эту библиотеку:
http://json.codeplex.com/
Это даст вам достаточный контроль над вашей сериализацией JSON.
Представьте, что у вас есть объект в ранние дни.
public class Customer
{
public string Name;
public string LastName;
}
После сериализации он будет выглядеть следующим образом:
{Имя: "John", LastName: "Doe" }
Если вы измените определение объекта для добавления/удаления полей. Сепаратизация будет выполняться гладко, если вы используете, например, JavaScriptSerializer.
public class Customer
{
public string Name;
public string LastName;
public int Age;
}
Если вы попытаетесь де-сериализовать последний json для этого нового класса, ошибка не будет выбрана. Дело в том, что ваши новые поля будут установлены по умолчанию. В этом примере: "Возраст" будет установлен на ноль.
Вы можете включить в свои собственные соглашения поле, присутствующее во всех ваших объектах, которое содержит номер версии. В этом случае вы можете указать разницу между пустым полем или несоответствием версии.
Итак, давайте скажем: у вас есть класс Customer v1 serialized:
{ 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;
}
Затем вы можете получить доступ к этим метаданным и сделать все, что вам может понадобиться.
Ответ 4
Не имеет значения, какой протокол сериализации вы используете, методы API-интерфейсов версий, как правило, одинаковы.
Обычно вам нужно:
- способ для потребителя сообщать производителю версию API, которую он принимает (хотя это не всегда возможно)
- способ для производителя встраивать информацию о версировании в сериализованные данные
- обратная совместимая стратегия для обработки неизвестных полей
В веб-API, как правило, версия API, которую потребитель принимает, встроена в заголовок Accept (например, Accept: application/vnd.myapp-v1+json application/vnd.myapp-v2+json
означает, что потребитель может обрабатывать как версию 1, так и версию 2 вашего API) или реже в URL (например, https://api.twitter.com/1/statuses/user_timeline.json
). Обычно это используется для основных версий (т.е. Обратных несовместимых изменений). Если на сервере и на клиенте нет соответствующего заголовка Accept, то связь не выполняется (или выполняется с максимальной эффективностью или возвращается к базовому протоколу по умолчанию в зависимости от характера приложения).
Затем производитель генерирует сериализованные данные в одной из запрошенных версий, а затем встраивает эту информацию в сериализованные данные (например, как поле с именем version
). Потребитель должен использовать информацию о версии, встроенную в данные, чтобы определить, как разбирать сериализованные данные. Информация о версии в данных также должна содержать незначительную версию (т.е. Для обратных совместимых изменений), обычно потребители должны иметь возможность игнорировать информацию о малой версии и все равно обрабатывать данные правильно, хотя понимание второстепенной версии может позволить клиенту сделать дополнительные предположения о как данные должны обрабатываться.
Общей стратегией обработки неизвестных полей является то, как анализируются HTML и CSS. Когда потребитель видит неизвестные поля, они должны игнорировать его, а когда в данных отсутствует поле, ожидаемое клиентом, оно должно использовать значение по умолчанию; в зависимости от характера связи, вы также можете указать некоторые обязательные поля (т.е. отсутствующие поля считаются фатальной ошибкой). Поля, добавленные в малые версии, всегда должны быть необязательным полем; второстепенная версия может добавлять необязательные поля или изменять поля семантики, если она совместима с обратными, в то время как основная версия может удалять поля или добавлять обязательные поля или изменять семантические поля в обратном несовместимом виде.
В расширяемом формате сериализации (например, JSON или XML) данные должны быть самоописательными, другими словами, имена полей всегда должны храниться вместе с данными; вы не должны полагаться на конкретные данные, доступные на определенных позициях.