- JSON désérialisation avec JSON.net: résultats mise en cache - Licence Fondamentale d'Informatique
mardi 27 août 2013

JSON désérialisation avec JSON.net: résultats mise en cache


Le 22 Janvier, j'ai promis que cela soit une série en trois parties. J'ai été un peu occupé avec les applications de mise à niveau, Windows 8 expériences et trivial ;-) trucs comme camps de code, un MVP Summit, la préparation de mon premier et second discours sur Windows Phone et autres joyeusetés et fait attendre pour la partie finale pour trois mois exactement - mais ceux qui me connaissent, savent que je tiens mes promesses, alors voici la troisième et dernière partie de mon JSON pour Windows Phone série.
Dans la partie 1 de cette série que j'ai décrit les bases de la création de classes à partir d'une chaîne JSON et puis tout simplement désérialisant la chaîne dans un (Liste des) cours. Dans la partie 2 , j'ai montré comment utiliser les sous-classes de JSONConverter pour gérer trucs complexes désérialiseur ne peut pas traiter de la boîte, comme des hiérarchies de classes. Partie 3, comme promis, montre une manière de cache des résultats - ce qui rend l'application plus rapide, plus réactif et plus de batterie / plan de données conviviale.
Utilisation de la solution de démonstration de la partie 2 comme point de départ, j'ai fait en premier dans mabibliothèque wp7nl sur CodePlex utilisant NuGet. Je suis paresseux comme tout programmeur (devrait être) et je tiens à reporter autant soulever de lourdes charges au code déjà existant que je peux ;-).
Lorsque vous traitez avec des données téléchargées à partir du Web tout devient asynchrone par nature (au moins sur Windows Phone), et puisque nous n'avons pas le attendre et claviers asynchrones à bord de notre plate-forme encore, j'ai tendance à utiliser observables de Microsoft.Phone.Reactive. Pour laisser ce travail à des événements, j'ai besoin d'une classe enfant EventArgs pour mon "chargement terminé" délégué, que j'ai défini de la manière triviale suivante:
using System;
utilisant System.Collections.Generic;

Wp7nl.Utilities d'espace de noms
{
  public class DataLoadCompletedArgs <T>: EventArgs
  {
    publique IList <T> Résultat {get; fixer;}

    Exception publique erreur {get; fixer;}
  }
}
Il s'agit d'une classe générique parce que je suis fondamentalement trop paresseux pour écrire des déclarations de coulée partout. La configuration de base de la classe d'assistance qui fait à la fois le chargement lui-même est comme ceci:
utilisant System.Linq;
utilisant Microsoft.Phone.Reactive;
using System;
utilisant System.Collections.Generic;
using System.ComponentModel;
using System.Net;
utilisant Newtonsoft.Json;

Wp7nl.Utilities d'espace de noms
{
  public class CachedServiceDataLoader <T> où T: classe
  {
    privé en lecture seule IsolatedStorageHelper <Liste <T>> storageHelper;

    CachedServiceDataLoader publique ()
    {
      storageHelper = new IsolatedStorageHelper <Liste <T>> ();
    }

    private void FireDataLoadCompleted (IList <T> résultat, une erreur d'exception)
    {
      if (DataLoadCompleted! = null)
      {
        DataLoadCompleted (ce qui,
                          nouveau DataLoadCompletedArgs <T> 
                          {Result = result, Error = erreur});
      }
    }

    publique DataLoadCompletedHandler void délégué (object sender, 
      DataLoadCompletedArgs <T> args);

    DataLoadCompletedHandler de manifestation publique DataLoadCompleted;
  }
}
Alors qu'avons-nous ici? Un constructeur qui crée un IsolatedStorageHelper - c'est une classe à partir de la dernière version de Wp7nl, où il se trouve dans l'espace de Wp7nl.Utilities. Il s'agit essentiellement de la logique interne des méthodes d'extension que j'ai décrit dans mon article sur tombstoning MVVMLight ViewModels utilisantSilverlightSerializer couper lâche de ces méthodes d'extension - de sorte qu'ils peuvent être utilisés pour mettre en cache toutes sortes de classes, et pas seulement ViewModels sur la désactivation ou la fermeture de l'application .La partie inférieure de la classe n'est que la manifestation indiquant une action de chargement des données est terminée, et une méthode pratique pour facilement tirer de cet événement (je l'ai mentionné que j'étais paresseux, n'ai-je pas? ;-)).
Ensuite, il ya un autre petit méthodes pratiques pour le chargement des trucs à partir du cache ou de retourner une liste par défaut (vide) si les résultats ne sont pas disponibles:
Liste privée <T> LoadFromStorage ()
{
  retourner storageHelper.ExistsInStorage (?) 
     storageHelper.RetrieveFromStorage (): nouvelle liste <T> ();
}
Il a utilisé qu'une seule fois, donc je ne pouvais tout simplement une simple omis, mais il fait le reste du code un peu plus lisible et c'est important aussi. Lorsque vous déclarez vos intentions dans le code, qui permet d'économiser sur le commentaire.
La méthode pour lire des trucs de cache est un peu plus complexe que le strict nécessaire, mais puisque les données provenant du web est à venir en asynchrone, pourquoi voudrais-je mon application à attendre sur mon cache crachats de ses résultats? J'ai donc fait la récupération de la mémoire cache asynchrone ainsi, en utilisant certains old skool BackgroundWorker battage:
public bool StartLoadFromCache ()
{
  si (storageHelper.ExistsInStorage ())
  {
    var w = new BackgroundWorker ();
    w.DoWork + = (s, e) =>
      {
        e.Result = LoadFromStorage ();
      };

    w.RunWorkerCompleted + = (s, e) =>
      FireDataLoadCompleted (e.Result comme List <T>, e.Error);

    w.RunWorkerAsync ();
    return true;
  }
  return false;
}
J'aurais pu utiliser une observable ici aussi je suppose, mais je n'ai toujours pas adapté mon extrait de code. De toute façon, je triche un peu en vérifiant d'abord si il ya toutes les données du cache du tout - s'il n'y a pas, la méthode renvoie immédiatement false, informant la méthode d'appel il n'ya pas de données mises en cache et d'aller chercher les trucs sur le web. Laquelle il peut, d'ailleurs, en appelant la méthode suivante:
vide StartDownloadCacheData (IList <T> currentObjects publics, Uri ServiceUri,
                                   params JsonConverter [] convertisseurs)
{
  var w = new SharpGIS.GZipWebClient ();
  Observable.FromEvent <DownloadStringCompletedEventArgs> (
    w ", DownloadStringCompleted")
    . S'abonner (r =>
    {
      if (DataLoadCompleted! = null)
      {
        if (r.EventArgs.Error == null)
        {
          var = désérialisée 
               JsonConvert.DeserializeObject <Liste <T>> (r.EventArgs.Result,
                                                      convertisseurs);
          var result = new List <T> (currentObjects);
          result.AddRange (deserialized.Where (p => Les currentObjects.Contains (p))!);
          FireDataLoadCompleted (résultat, r.EventArgs.Error);
          storageHelper.SaveToStorage (result);
        }
        autre
        {
          FireDataLoadCompleted (null, r.EventArgs.Error);
        }
      }
    });

  w.DownloadStringAsync (serviceUri);
}
Alors que cette méthode fait est assez simple:
  • Il fait un GZipWebClient (qui permet aux données d'être chargées zippé, sauvant ainsi la quantité d'octets transmis)
  • Il fait une observable de l'événement "DownloadStringCompleted" et souscrit une méthode anonyme
  • Il tire la méthode DownloadStringAsync sur l'URI
Maintenant, la méthode anonyme qui est déclenché lorsque les données arrivent
  • Vérifie les erreurs
  • Essaie de désérialiser les données entrantes utilisant des convertisseurs prévoit (le cas échéant)
  • Ajoute la liste d'objets existant à la liste des résultats
  • Ajouter les nouveaux objets à la liste après le désherbage des doublons qui étaient déjà dans la liste
  • Déclenche l'événement complet
  • Stocke l'ensemble des données dans le stockage isolé maintenant fusionnées.
Donc, l'idée est que vous pouvez faire de téléchargements consécutifs, mais que les données obtenues ne contient jamais les doublons. C'est pourquoi vous devez fournir la liste des actuels objets. Cela peut sembler un peu bizarre, mais c'est exactement la chose que vous voulez faire avec l'utilisation de données géographiques - ce que je fais presque tout le temps pour gagner sa vie. Comme un inspecteur vous voulez télécharger trucs des zones que vous souhaitez contrôler, et non l'ensemble de la municipalité. Vous déplacez votre carte sur un endroit, cliquez sur "télécharger" et hop, vous avez les trucs que vous souhaitez utiliser dispositif mis en cache sur vous avant de vous rendre sur la route. Répétez jusqu'à ce que toutes les zones que vous souhaitez visiter aujourd'hui ont été traitées.Un peu comme Nokia Drive fait - il n'y a jamais assez de place à bord de votre téléphone pour stocker toutes les cartes possibles du monde, mais quand tu Seattle, de télécharger les cartes pour l'État de Washington, et non pas l'ensemble de l'Amérique du Nord, et vous êtes bon aller.
Mais que faire si vous voulez recommencer - vider le cache au lieu de l'ajouter? C'est la dernière méthode de cette classe:
StartClearStorage public void ()
{
  var w = new BackgroundWorker ();
  w.DoWork + = (s, e) => storageHelper.DeletedFromStorage ();

  w.RunWorkerCompleted + = (s, e) =>
  {
    if (DataLoadCompleted! = null)
    {
      FireDataLoadCompleted (nouvelle liste <T> (), e.Error);
    }
  };

  w.RunWorkerAsync ();
}
Également de manière asynchrone, également pas strictement nécessaire, mais Microsoft ont tendance à se déplacer vers tout faire de manière asynchrone - si vous avez fait tout le développement de Windows 8, vous savez exigences de performance sont assez élevées et strictes, afin de mieux s'habituer à elle déjà.
JSONdemo3J'ai mis à jour la solution démo pour montrer les rouages ​​de cette classe, qui présente lui-même comme montré à droite. Si vous cliquez sur "dispositifs de charge", il affichera quatre dispositifs, comme affichés sur l'image et le message "Chargement du web". Si vous appuyez de nouveau sur le bouton "dispositifs de charge", il affichera les quatre mêmes appareils, mais affiche le message "Chargement du cache" et il va leur montrer un tout petit peu plus vite.
Si vous cliquez sur "Charger plus d'appareils", vous verrez six appareils, - avec le Lumia 800 mentionné deux fois. Mais attendez, qu'est-ce au sujet de désherbage les doublons dans StartDownloadCacheData - n'est pas que le travail? Il fait sûr, mais je l'ai déjà mentionné, je suis paresseux, je n'ai donc pas mis en œuvre une logique "égale" sur la classe de l'appareil - ce qui reste comme exercice pour le lecteur ;-). Je peux vous assurer qu'il fonctionne correctement alors.
"Effacer la liste" efface seulement la liste, et si vous cliquez sur "dispositifs de charge», il va charger les dispositifs de cache à nouveau - très rapide.
"Effacer le cache et la liste" seront effectivement effacer le cache, donc si vous cliquez sur "dispositifs de charge" à nouveau il va voir "Chargement du web".
Je ne vais pas vous ennuyer avec le XAML - le code dans MainPage.xaml.cs est assez simple et met en valeur toutes les caractéristiques de la CachedServiceDataLoader:
using System;
utilisant System.Collections.Generic;
utilisant System.Windows;
utilisant Microsoft.Phone.Controls;
utilisant Microsoft.Phone.Reactive;
utilisant Wp7nl.Utilities;

namespace JsonDemo
{
  publique MainPage de classe partielle: PhoneApplicationPage
  {
    CachedServiceDataLoader privé <Device> cachedLoader;
    IList privé <Device> CurrentData;
    MainPage publique ()
    {
      InitializeComponent ();
      Loaded + = MainPage_Loaded;
    }

    annuler MainPage_Loaded (object sender, RoutedEventArgs e)
    {
      cachedLoader = new CachedServiceDataLoader <Device> ();
      CurrentData = new List <Device> ();
      Observable.FromEvent <DataLoadCompletedArgs <Device>> (
        cachedLoader, "DataLoadCompleted")
       . S'abonner (r =>
                    {
                      CurrentData = r.EventArgs.Result;
                      PhoneList.ItemsSource = r.EventArgs.Result;
                    });
    }

    private void Load_Click (object sender, RoutedEventArgs e)
    {
      if (! cachedLoader.StartLoadFromCache ())
      {
        Message.Text = "Chargement du web";
        cachedLoader.StartDownloadCacheData (CurrentData,
          new Uri ("http://www.schaikweb.net/dotnetbyexample/JSONPhones2.txt"),
          nouveau JsonDeviceConverter (), nouveau JsonSpecsConverter ());
      }
      autre
      {
        Message.Text = "Chargement du cache";        
      }
    }

    private void Load2_Click (object sender, RoutedEventArgs e)
    {
      Message.Text = "Chargement du web";
      cachedLoader.StartDownloadCacheData (CurrentData,
        new Uri ("http://www.schaikweb.net/dotnetbyexample/JSONPhones3.txt"),
        nouveau JsonDeviceConverter (), nouveau JsonSpecsConverter ());
    }      

    private void Clear_Click (object sender, RoutedEventArgs e)
    {
      PhoneList.ItemsSource = null;
      Message.Text = "Liste dégagé (pas de cache)";
    }

    private void ClearCache_Click (object sender, RoutedEventArgs e)
    {
      cachedLoader.StartClearStorage ();
      Message.Text = "liste et cache effacée";
    }
  }
}
Il n'y a même pas de ma marque code MVVM ici. Dans le MainPage_Loaded - sans surprise - tout le truc est initialisée. Le CachedServiceDataLoader est créé en utilisant un type T, et crache un IList de T. J'utilise une observable de garder une trace des choses ici aussi, mais bien sûr, vous êtes libre de vous abonner à des événements de la manière «old fashioned».
Load_Click montre l'utilisation de StartLoadFromCache et StartDownloadCacheData, cette dernière utilisant les classes d'enfants de JSONconverter je l'ai montré dans la partie 2. Le reste, je suppose être assez simple.
Comme vous pouvez le voir, en collaboration avec JSON sur Windows Phone est mort facile et ma petite classe d'assistance qui rend encore plus facile. Je réfléchissais encore si je dois inclure dans la bibliothèque wp7nl, car cela crée encore deux autres dépendances (SharpGIS.GZipWebClient et Newtonsoft.Json) pour rester en phase. Si vous avez des commentaires à ce sujet, je serais heureux de l'entendre. Mais de toute façon, je termine mon JSON pour la série Windows Phone, j'espère que cela se révélera être un mini-tutoriel utile pour la communauté des développeurs Windows Phone. Et comme d'habitude, vous pouvez télécharger la solution complète de mon site.

0 commentaires:

Enregistrer un commentaire

 
-