Quel est le meilleur moyen de gérer les versions en utilisant le protocole JSON?

J’écris normalement toutes les parties du code en C # et lorsque j’écris des protocoles sérialisés, j’utilise FastSerializer qui sérialise / désérialise les classes rapidement et efficacement. Il est également très facile à utiliser et assez simple pour faire du “versioning“, c’est-à-dire pour gérer différentes versions de la sérialisation. La chose que j’utilise normalement ressemble à ceci:

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.ReadSsortingng(); this.HomeAddress = reader.ReadSsortingng(); this.ZipCode = reader.ReadSsortingng(); this.HomeCity = reader.ReadSsortingng(); if (serializeVersion > 0) this.HomeAddressObj = reader.ReadUInt32(); if (serializeVersion > 1) this.County = reader.ReadSsortingng(); if (serializeVersion > 2) this.Muni = reader.ReadSsortingng(); if (serializeVersion > 3) this._AvailableCustomers = reader.ReadList(); } 

et

 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(); writer.Write(CustomerCards); writer.Write(HomeAddressObj); writer.Write(County); // v 2 writer.Write(Muni); // v 4 if (_AvailableCustomers == null) _AvailableCustomers = new List(); writer.Write(_AvailableCustomers); } 

Donc, il est facile d’append de nouvelles choses, ou de changer complètement la sérialisation si l’on choisit de le faire.

Cependant, je veux maintenant utiliser JSON pour des raisons qui ne sont pas pertinentes ici =) J’utilise actuellement DataContractJsonSerializer et je cherche maintenant un moyen d’avoir la même flexibilité que j’ai avec FastSerializer ci-dessus.

Donc, la question est: Quel est le meilleur moyen de créer un protocole / sérialisation JSON et de pouvoir détailler la sérialisation comme ci-dessus, afin de ne pas casser la sérialisation simplement parce qu’une autre machine n’a pas encore mis à jour sa version?

La clé de la gestion des versions de JSON consiste à toujours append de nouvelles propriétés et à ne jamais supprimer ou renommer les propriétés existantes. Ceci est similaire à la façon dont les tampons de protocole traitent les versions .

Par exemple, si vous avez démarré avec le code JSON suivant:

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

Et vous voulez renommer la propriété “foo” en “bar”, ne vous contentez pas de la renommer. Ajoutez plutôt une nouvelle propriété:

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

Puisque vous ne supprimez jamais les propriétés, les clients basés sur des versions plus anciennes continueront à fonctionner. L’inconvénient de cette méthode est que le JSON peut être gonflé au fil du temps et que vous devez continuer à conserver les anciennes propriétés.

Il est également important de définir clairement vos cas «extrêmes» pour vos clients. Supposons que vous avez une propriété de tableau appelée “fooList”. La propriété “fooList” peut prendre les valeurs possibles suivantes: n’existe pas / undefined (la propriété n’est pas physiquement présente dans l’object JSON ou existe et est définie sur “undefined”), une liste vide ou vide ou une liste contenant une ou plusieurs valeurs. Il est important que les clients comprennent comment se comporter, en particulier dans les cas non définis / null / vides.

Je recommande également de lire comment fonctionne la gestion des versions sémantiques . Si vous introduisez un schéma de gestion de version sémantique dans vos numéros de version, vous pouvez modifier les versions compatibles sur une limite de version mineure, tout en modifiant les limites de la version principale (les clients et les serveurs doivent être d’accord sur la même version majeure). ). Bien que ce ne soit pas une propriété du JSON lui-même, cela est utile pour communiquer les types de modifications qu’un client doit attendre lorsque la version change.

La bibliothèque gson basée sur Java de Google prend en charge les versions de json. Cela peut s’avérer très pratique si vous pensez à la java.

Il y a un tutoriel sympa et facile ici .

N’utilisez pas DataContractJsonSerializer, comme son nom l’indique, les objects traités via cette classe devront:

a) Être marqué avec les atsortingbuts [DataContract] et [DataMember].

b) être ssortingctement conforme au «contrat» défini, c’est-à-dire rien de moins et rien de plus qu’il n’est défini. Tout élément supplémentaire ou manquant [DataMember] effectuera la désérialisation pour générer une exception.

Si vous voulez être suffisamment flexible, utilisez le JavaScriptSerializer si vous voulez opter pour l’option bon marché … ou utilisez cette bibliothèque:

http://json.codeplex.com/

Cela vous donnera un contrôle suffisant sur votre sérialisation JSON.

Imaginez que vous ayez un object à ses débuts.

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

Une fois sérialisé, il ressemblera à ceci:

{Nom: “John”, Nom: “Doe”}

Si vous modifiez la définition de votre object pour append / supprimer des champs. La désérialisation se fera en douceur si vous utilisez, par exemple, JavaScriptSerializer.

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

Si vous essayez de désérialiser le dernier json à cette nouvelle classe, aucune erreur ne sera lancée. La chose est que vos nouveaux champs seront mis à leurs valeurs par défaut. Dans cet exemple: “Age” sera mis à zéro.

Vous pouvez inclure, dans vos propres conventions, un champ présent dans tous vos objects, qui contient le numéro de version. Dans ce cas, vous pouvez faire la différence entre un champ vide ou une incohérence de version.

Alors disons: Vous avez votre classe Customer v1 sérialisée:

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

Vous souhaitez désérialiser dans une instance Customer v2, vous aurez:

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

Vous pouvez d’une manière ou d’une autre, détecter quels champs de votre object sont en quelque sorte fiables et ce qui ne l’est pas. Dans ce cas, vous savez que votre instance d’object v2 provient d’une instance d’object v1, de sorte que le champ Age ne doit pas être approuvé.

Je pense que vous devez également utiliser un atsortingbut personnalisé, par exemple “MinVersion”, et marquer chaque champ avec le numéro de version minimum pris en charge, pour obtenir quelque chose comme ceci:

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

Ensuite, vous pouvez accéder à ces métadonnées et faire tout ce dont vous avez besoin.

Peu importe le protocole de sérialisation que vous utilisez, les techniques de version des API sont généralement les mêmes.

En général, vous avez besoin de:

  1. un moyen pour le consommateur de communiquer au producteur la version de l’API qu’il accepte (bien que ce ne soit pas toujours possible)
  2. un moyen pour le producteur d’intégrer des informations de version dans les données sérialisées
  3. une stratégie compatible en amont pour gérer les champs inconnus

Dans une API Web, la version de l’API Accept: application/vnd.myapp-v1+json application/vnd.myapp-v2+json le consommateur est généralement intégrée à l’en-tête Accept (par exemple, Accept: application/vnd.myapp-v1+json application/vnd.myapp-v2+json signifie que le consommateur peut gérer la version 1 et la version 2 de votre API) ou moins fréquemment dans l’URL (par exemple https://api.twitter.com/1/statuses/user_timeline.json ). Ceci est généralement utilisé pour les versions majeures (c’est-à-dire les modifications incompatibles vers l’arrière). Si le serveur et le client ne disposent pas d’un en-tête Accept correspondant, la communication échoue (ou se déroule selon le meilleur effort ou selon un protocole de base par défaut, selon la nature de l’application).

Le producteur génère ensuite une donnée sérialisée dans l’une des versions demandées, puis incorpore cette information de version dans les données sérialisées (par exemple, en tant que champ nommé version ). Le consommateur doit utiliser les informations de version intégrées aux données pour déterminer comment parsingr les données sérialisées. Les informations de version dans les données doivent également contenir une version mineure (par exemple, pour les modifications rétrocompatibles). En règle générale, les consommateurs peuvent ignorer les informations de version mineure tout en traitant correctement les données. comment les données doivent être traitées.

Une stratégie courante pour gérer les champs inconnus est comme la façon dont HTML et CSS sont analysés. Lorsque le consommateur voit un champ inconnu, il doit l’ignorer et, lorsqu’il manque un champ attendu par le client, il doit utiliser une valeur par défaut. Selon la nature de la communication, vous pouvez également spécifier certains champs obligatoires (les champs manquants sont considérés comme une erreur fatale). Les champs ajoutés dans les versions mineures doivent toujours être des champs facultatifs; La version mineure peut append des champs optionnels ou modifier la sémantique des champs tant que celle-ci est rétrocompatible, tandis que la version majeure peut supprimer des champs ou append des champs obligatoires ou modifier les champs sémantiquement d’une manière incompatible.

Dans un format de sérialisation extensible (comme JSON ou XML), les données doivent être auto-descriptives, en d’autres termes, les noms de champs doivent toujours être stockés avec les données; vous ne devez pas vous fier aux données spécifiques disponibles sur des positions spécifiques.