Il presente articolo è stato tradotto automaticamente.

JSON

Analisi di stringhe JSON in componenti Windows Runtime

Craig Shoemaker

Scarica il codice di esempio

Windows Store apps costruito con JavaScript lasciare qualcuno con HTML e JavaScript abilità costruire applicazioni native per Windows, ma JavaScript non è sempre la scelta migliore per risolvere ogni problema.Qualche comportamento nelle applicazioni potrebbe essere meglio implementato in maniera più orientate agli oggetti utilizzando c#, Visual Basic o C++.Inoltre, alcuni aspetti del codice potrebbero essere un candidato per il riutilizzo tra più componenti di Windows Runtime (WinRT) che richiedono dati dal livello di interfaccia utente.Passaggio di dati da JavaScript in componenti WinRT e tornare all'interfaccia utente è importante per capire in una di queste situazioni.

Sul Web, i dati spesso sono passati dal client al server e torna in forma di oggetti JSON.In diversi contesti, quadri come ASP.NET Web Form e MVC ASP.NET includono funzionalità quali leganti modello o almeno una sorta di "auto-magia" movimentazione sul lato server per l'analisi di oggetti JSON.WinRT componenti sono oggetti con affordances per l'analisi di JSON, ma il supporto è a basso livello e più snella interazione richiede una qualche gestione esplicita da parte vostra.

In questo articolo viene illustrato come analizzare in modo affidabile stringhe JSON passati WinRT componenti al fine di idratare gli oggetti fortemente tipizzati e restituiscono un risultato indietro fino all'interfaccia utente.

Restrizioni per interazione con componenti WinRT

Prima di discutere i particolari di oggetti JSON di analisi, deve prima conoscere requisiti e restrizioni per l'interazione con componenti WinRT.L'argomento della guida MSDN, "Creazione di Windows componenti Runtime in C# e Visual Basic(bit.ly/WgBBai), nei dettagli ciò che è necessario per WinRT componenti per quanto riguarda la dichiarazione di parametro del metodo e tipi restituiti.Ammessi tipi sono in gran parte composti di tipi primitivi e una manciata di tipi di insieme, così il tentativo di passare un oggetto JSON crudo in un componente non è consentita.Il modo migliore per passare un oggetto JSON in un componente gestito è prima serializzare l'oggetto JSON (utilizzando il metodo JSON. stringify), come le stringhe sono pienamente supportate in queste classi.

L'analisi di oggetti JSON in codice gestito

Lo spazio dei nomi Windows.Data.Json comprende un certo numero di differenti classi progettate per lavorare con oggetti JSON in maniera fortemente tipizzata, incluse le classi JsonValue, JsonArray e JsonObject.La classe JsonValue rappresenta un valore JSON esposto in forma di stringa, numero, Boolean, array o oggetto (vedere di più su questo a bit.ly/14AcTmF).Analisi di una stringa JSON richiede che si passa la stringa raw to JsonValue, che quindi è in grado di restituire un'istanza di JsonObject.

Classe JsonObject rappresenta un oggetto JSON completo e include metodi per modificare l'oggetto di origine.Tramite la classe JsonObject, si può aggiungere e rimuovere membri, estrarre i dati dai membri, scorrere ogni membro e anche serializzare l'oggetto nuovamente.Maggiori dettagli su JsonObject sono disponibili presso bit.ly/WDWZkG.

La classe JsonArray rappresenta una matrice JSON che, ancora una volta, comprende una serie di metodi per il controllo della matrice, ad esempio di iterazione e aggiunta e rimozione di elementi della matrice.Ulteriori informazioni sull'interfaccia della classe JsonArray sono disponibile presso bit.ly/XVUZo1.

Ad esempio per vedere come iniziare a utilizzare queste classi, si consideri il seguente oggetto JSON in JavaScript:

{
  firstName: "Craig"
}

Prima di tentare di passare l'oggetto a un componente di WinRT, è necessario serializzare l'oggetto in una stringa utilizzando la funzione JSON. stringify. Si noti che cosa succede dopo che l'oggetto viene serializzato — l'oggetto stesso è rappresentato come segue:

"{
  '_backingData': {
    'firstName': 'Craig'
  },
  'firstName': 'Craig',
  'backingData': {
    'firstName':'Craig'}
}"

Questo potrebbe essere una sorpresa per voi, come la stessa chiamata di funzione in un browser Web semplicemente serializza l'oggetto in una stringa senza l'aggiunta di tutti i membri dell'oggetto. Questo cambiamento nella struttura della stringa JSON influisce su come estrarre i dati dall'oggetto.

Il primo passo nella lettura di questi dati in un componente di WinRT è tentare di analizzare la stringa in arrivo come istanza JsonValue. Se tale analisi ha esito positivo, è possibile richiedere JsonObject dall'istanza JsonValue radice. In questo caso il JsonValue è l'oggetto radice come creato dalla chiamata alla funzione stringify, e JsonObject consente di che accedere all'oggetto originale che hai iniziato con JavaScript.

Nell'esempio di codice riportato di seguito descrive come, una volta JsonObject è disponibile, è possibile utilizzare il metodo GetNamedString per estrarre il valore del membro "firstName" in una variabile:

JsonValue root;
JsonObject jsonObject;
string firstName;
if (JsonValue.TryParse(jsonString, out root))
{
  jsonObject = root.GetObject();
  if (jsonObject.ContainsKey("firstName"))
  {
    firstName = jsonObject.GetNamedString("firstName");
  }
}

Un approccio simile è usato per accedere ai membri booleani e numerici — dove sono disponibili metodi GetNamedBoolean e GetNamedNumber. Il passo successivo è quello di implementare i metodi di estensione di JsonObject al fine di rendere più facile accedere ai dati JSON.

Metodi di estensione per JsonObject

L'implementazione predefinita della classe JsonObject fornisce qualche comportamento a basso livello che è notevolmente migliorato con alcuni metodi semplici che possono gestire formattazione imperfetta e può evitare eccezioni se membri non esistono nell'origine. In altre parole, gli oggetti creati in JavaScript sono associati a problemi strutturali o di formattazione che potrebbero causare eccezioni. Aggiungendo i seguenti metodi di estensione per la classe JsonObject aiuterà ad attenuare questi problemi.

Il primo metodo di estensione per aggiungere è chiamato GetStringValue. Figura 1 viene illustrata l'implementazione, che in primo luogo i controlli per verificare che il membro presente sull'oggetto. In questo caso, il parametro chiave è il nome della proprietà dell'oggetto JSON. Dopo che il membro è conosciuto per esistere, quindi il metodo TryGetValue è utilizzato per tentare di accedere ai dati dall'istanza JsonObject. Se il valore viene individuato correttamente, viene restituito come un oggetto che implementa l'interfaccia IJsonValue.

Implementazione del metodo GetStringValue estensione figura 1

public static string GetStringValue(this JsonObject jsonObject, 
  string key)
{
  IJsonValue value;
  string returnValue = string.Empty;
  if (jsonObject.ContainsKey(key))
  {
    if (jsonObject.TryGetValue(key, out value))
    {
      if (value.ValueType == JsonValueType.String)
      {
        returnValue = jsonObject.GetNamedString(key);
      }
      else if (value.ValueType == JsonValueType.Number)
      {
        returnValue = jsonObject.GetNamedNumber(key).ToString();
      }
      else if (value.ValueType == JsonValueType.Boolean)
      {
        returnValue = jsonObject.GetNamedBoolean(key).ToString();
      }
    }
  }
  return returnValue;
}

L'interfaccia IJsonValue include la proprietà ValueType read-only, che espone il valore dell'enumerazione JsonValueType che indica il tipo dell'oggetto dati. Dopo interrogare ValueType, viene utilizzato il metodo opportunamente tipizzato per estrarre i dati dall'oggetto.

Il metodo GetStringValue comprende consapevolezza di valori booleani e valori numerici al fine di premunirsi contro oggetti JSON non valido. È possibile scegliere di rendere l'implementazione più severe e rinunciare l'analisi o generare un errore se l'oggetto JSON rigorosamente non è formattato per il tipo previsto, ma il codice nel mio esempio rende l'operazione di analisi flessibili e protezioni contro gli errori.

Il successivo metodo di estensione, visto Figura 2, è l'implementazione per estrarre i valori booleani. In questo caso, valori booleani, espressi come stringhe (ad esempio, "1" o "vero" per un vero valore e così via) e numeri (ad esempio, "1" per true e "0" o false) sono supportati nel metodo GetBooleanValue.

Figura 2 implementazione del metodo di estensione GetBooleanValue

public static bool?
GetBooleanValue(this JsonObject jsonObject, 
  string key)
{
  IJsonValue value;
  bool?
returnValue = null;
  if (jsonObject.ContainsKey(key))
  {
    if (jsonObject.TryGetValue(key, out value))
    {
      if (value.ValueType == JsonValueType.String)
      {
        string v = jsonObject.GetNamedString(key).ToLower();
        if (v == "1" || v == "true")
        {
          returnValue = true;
        }
        else if (v == "0" || v == "false")
        {
          returnValue = false;
        }
      }
      else if (value.ValueType == JsonValueType.Number)
      {
        int v = Convert.ToInt32(jsonObject.GetNamedNumber(key));
        if (v == 1)
        {
          returnValue = true;
        }
        else if (v == 0)
        {
          returnValue = false;
        }
      }
      else if (value.ValueType == JsonValueType.Boolean)
      {
        returnValue = value.GetBoolean();
      }
    }
  }
  return returnValue;
}

L'estensione numerica metodi sono impostati per restituire i tipi nullable, quindi in questo caso, GetDoubleValue restituisce un valore double nullable. Il comportamento correttivo in questo caso tenta di convertire stringhe in valori numerici corrispondenti possibili (vedere Figura 3).

Figura 3 implementazione del metodo di estensione GetDoubleValue

public static double?
GetDoubleValue(this JsonObject jsonObject, 
  string key)
{
  IJsonValue value;
  double?
returnValue = null;
  double parsedValue;
  if (jsonObject.ContainsKey(key))
  {
    if (jsonObject.TryGetValue(key, out value))
    {
      if (value.ValueType == JsonValueType.String)
      {
        if (double.TryParse(jsonObject.GetNamedString(key), 
          out parsedValue))
        {
          returnValue = parsedValue;
        }
      }
      else if (value.ValueType == JsonValueType.Number)
      {
        returnValue = jsonObject.GetNamedNumber(key);
      }
    }
  }
  return returnValue;
}

Perché il metodo incorporato per estrarre i numeri della classe JsonObject restituisce un valore double, e spesso i valori dati sono espressi come numeri interi, il codice riportato di seguito viene illustrato come il metodo GetIntegerValue esegue il wrapping del metodo GetDoubleValue e converte il risultato in un valore integer:

public static int?
GetIntegerValue(this JsonObject jsonObject, 
  string key)
{
  double?
value = jsonObject.GetDoubleValue(key);
  int?
returnValue = null;
  if (value.HasValue)
  {
    returnValue = Convert.ToInt32(value.Value);
  }
  return returnValue;
}

Aggiunta del supporto di fabbrica

Ora che la classe JsonObject è estesa per includere alcuni superiore-­supporto specialistico per l'estrazione di dati in tipi primitivi, il passo successivo è di utilizzare questo supporto in classi factory che sono responsabili per la presa in ingresso stringhe JSON e restituendo un'istanza di un oggetto di dominio idratati.

Nell'esempio di codice riportato di seguito descrive come una persona è modellata nel sistema:

internal class Person
{
  public int Id { get; set; }
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public bool?
IsOnWestCoast { get; set; }
}

Il codice riportato di seguito viene illustrato il metodo Create in persona­classe Factory che accetta una stringa:

 

public static Person Create(string jsonString)
{
  JsonValue json;
  Person person = new Person();
  if (JsonValue.TryParse(jsonString, out json))
  {
    person = PersonFactory.Create(json);
  }
  return person;
}

Figura 4 di seguito viene illustrato un metodo Create che accetta un JsonValue. Questi metodi Create usati insieme sono responsabili per scattare in raw stringa e restituire un'istanza della classe persona con i dati previsti in ogni membro. I metodi sono separati e sovraccarico al fine di fornire supporto per le matrici di JSON, che è spiegato nella sezione successiva.

Figura 4 PersonFactory creare metodo che accetta un JsonValue

public static Person Create(JsonValue personValue)
{
  Person person = new Person();
  JsonObject jsonObject = personValue.GetObject();
  int?
id = jsonObject.GetIntegerValue("id");
  if (id.HasValue)
  {
    person.Id = id.Value;
  }
  person.FirstName = jsonObject.GetStringValue("firstName");
  person.LastName = jsonObject.GetStringValue("lastName");
  bool?
isOnWestCoast = jsonObject.GetBooleanValue("isOnWestCoast");
  if (isOnWestCoast.HasValue)
  {
    person.IsOnWestCoast = isOnWestCoast.Value;
  }
  return person;
}

Aggiunta del supporto di matrice

A volte i dati viene fornito sotto forma di matrici di oggetti, piuttosto che solo singoli oggetti. In questo caso deve tentare di analizzare la stringa come matrice sfruttando la classe JsonArray. Figura 5 Mostra come la stringa in ingresso viene analizzata in una matrice e quindi ogni elemento viene passato al metodo Create per l'analisi finale nel modello. Si noti che così che in caso di non analizza la stringa in una matrice di oggetti, il risultato è una matrice vuota, che aiuta a evitare eccezioni inaspettate prima è creata una nuova istanza dell'elenco persona.

Figura 5 PersonFactory CreateList Metodo.

public static IList<Person> CreateList(string peopleJson)
{
  List<Person> people = new List<Person>();
  JsonArray array = new JsonArray();
  if (JsonArray.TryParse(peopleJson, out array))
  {
    if (array.Count > 0)
    {
      foreach (JsonValue value in array)
      {
        people.Add(PersonFactory.Create(value));
      }
    }
  }
  return people;
}

Aggiungere le classi di supporto

Il passo successivo è quello di creare un oggetto che è responsabile dell'utilizzo della classe factory e facendo qualcosa di interessante con le istanze di modello risultante. Figura 6 dimostra come sia individuale e stringhe di matrice JSON sono consumati e poi manipolati come oggetti fortemente tipizzati.

Nella figura 6 implementazione ContactsManager (senza supporto di Async)

using System.Collections.Generic;
public sealed class ContactsManager
{
  private string AddContact(string personJson)
  {
    Person person = PersonFactory.Create(personJson);
    return string.Format("{0} {1} is added to the system.",
      person.FirstName,
      person.LastName);
  }
  private string AddContacts(string personJson)
  {
    IList<Person> people = PersonFactory.CreateList(personJson);
    return string.Format("{0} {1} and {2} {3} are added to the system.",
      people[0].FirstName,
      people[0].LastName,
      people[1].FirstName,
      people[1].LastName);
  }
}

Supporto di interazione asincrona

Chiamate di questa natura in metodi di WinRT componenti devono essere eseguite in modo asincrono, come i messaggi JSON hanno il potenziale per crescere in una dimensione arbitraria, che potrebbe introdurre latenza nell'applicazione.

Il codice riportato di seguito include il metodo aggiunto a ContactsManager per supportare l'accesso asincrono al metodo AddContact:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Windows.Foundation;
public IAsyncOperation<string> AddContactAsync(string personJson)
{
  return Task.Run<string>(() =>
  {
    return this.AddContact(personJson);
  }).AsAsyncOperation();
}

Il metodo AddContactAsync accetta la stringa JSON e quindi avvia un'attività, che fa il lavoro di esecuzione del metodo AddContact. Una volta completata l'operazione, viene inviata una risposta alla promessa di JavaScript semplificato mediante il supporto per l'interfaccia IAsyncOperation. Il codice sorgente completo della classe ContactsManager con supporto asincrono per entrambi AddContact e AddContacts può essere trovato nel download del codice.

Mantenere le promesse in JavaScript

L'ultimo pezzo del puzzle è quello di utilizzare la classe ContactsManager in JavaScript e richiami alla classe utilizzando il modello di promessa. L'approccio utilizzato in questo esempio è quello di implementare un modello di visualizzazione che passa i dati modellati al componente WinRT e quindi attende una risposta. I dati utilizzati per passare al componente sono definiti Figura 7, che include un singolo oggetto JSON come una matrice.

Figura 7 JSON Data Source

var _model = {
  contact: {
    id: 1000,
    firstName: "Craig",
    lastName: "Shoemaker"
  },
  contacts: [
    {
      id: 1001,
      firstName: "Craig",
      lastName: "Shoemaker",
      isOnWestCoast: "true"
    },
    {
      id: 1002,
      firstName: "Jason",
      lastName: "Beres",
      isOnWestCoast: "0"
    }
  ]
}

Il modello di visualizzazione, come si è visto nel Figura 8, include un membro per il modello e i membri per i messaggi restituiti dal componente WinRT. La libreria di Windows per framework JavaScript (WinJS) associazione viene utilizzata per associare i messaggi restituiti dalla risposta agli elementi HTML. Un listato completo del modulo pagina è disponibile nel download del codice, così potete vedere come tutti i singoli pezzi si incastrano.

Figura 8 Mostra modello che utilizza ContactsManager

var _vm = {
  ViewModel: WinJS.Binding.as({
    model: _model,
    contactMsg: "",
    contactsMsg: "",
    addContact: function () {
      var mgr = ParseJSON.Utility.ContactsManager();
      var jsonString = JSON.stringify(_vm.ViewModel.model.contact);
      mgr.addContactAsync(jsonString).done(function (response) {
        _vm.ViewModel.contactMsg = response;
      });
    },
    addContacts: function () {
      var mgr = ParseJSON.Utility.ContactsManager();
      var jsonString = JSON.stringify(_vm.ViewModel.model.contacts);
        mgr.addContactsAsync(jsonString).done(function (response) {
          _vm.ViewModel.contactsMsg = response;
        });
      }
  })
};

Si noti che se si desidera associare l'add­contatto o addContacts le funzioni di un tasto durante l'associazione dati, è necessario eseguire la funzione WinJS.Utilities.requireSupportedForProcessing, passando un riferimento alla funzione sul tuo modello di visualizzazione.

Il passo finale è quello di aggiungere gli elementi appropriati e gli attributi HTML per supportare l'associazione. Un elemento div funge da contenitore per gli elementi di associazione Associazione principale ed è contrassegnato dall'impostazione dati-win-bindsource = "Application.Pages.Home.View­modello." Quindi gli elementi header sono vincolati ai loro membri di dati specificando i valori appropriati per il vittoria-associare attributi:

<section aria-label="Main content" role="main">
  <div data-win-bindsource=
    "Application.Pages.Home.ViewModel">
    <h2 data-win-bind="innerText: contactMsg"></h2>
    <hr />
    <h2 data-win-bind="innerText: contactsMsg"></h2>
  </div>
</section>

Ecco fatto. Building Windows Store apps con JavaScript ti dà l'opportunità di sfruttare le vostre abilità esistente dal Web per costruire applicazioni di interfaccia utente native, moderni, ma ci sono un certo numero di fattori distintivi tra le due piattaforme. Low-livello supporto per l'analisi dei dati JSON è disponibile attraverso lo spazio dei nomi Windows.Data.Json, ma è possibile aggiungere il supporto più ricco con alcune estensioni agli oggetti esistenti.

Craig Shoemaker è uno sviluppatore di software, podcaster, blogger e technical evangelist.  Egli è anche un autore Code Magazine, MSDN e Pluralsight. Nel suo tempo libero si diverte cercando un pagliaio in cui nascondere la sua collezione di pregiato ago. È possibile contattarlo su Twitter a twitter.com/craigshoemaker.

Grazie ai seguenti esperti tecnici per la revisione di questo articolo: Christopher Bennage (Microsoft), Kraig Brockschmidt (Microsoft) e Richard Fricks (Microsoft)
Christopher Bennage è uno sviluppatore di Microsoft sui modelli & Squadra di pratiche. Suo compito è di scoprire, raccogliere e incoraggiare le pratiche che portano gli sviluppatori di gioia. Tra i suoi recenti interessi tecnici sono JavaScript e sviluppo del gioco (casual). Blog di lui a dev.bennage.com.

Kraig Brockschmidt ha lavorato con Microsoft dal 1988, concentrandosi su come aiutare gli sviluppatori attraverso la scrittura, educazione, parlare in pubblico e impegno diretto. Egli è un senior program manager del team di Windows ecosistema lavorando con partner chiave sulla creazione di applicazioni Windows Store e portando la conoscenza acquisita in quell'esperienza per la più ampia comunità di sviluppatori. Il suo libro più recente è "Programmazione Windows 8 Apps in HTML, CSS e JavaScript" (un ebook gratuito da Microsoft Press); Blog di lui a kraigbrockschmidt.com/blog.

Richard Fricks ha lavorato con la comunità di sviluppo per 20 anni, aiutando più di recente progettazione della strategia di API di Runtime di Windows Media e aiutare la comunità di sviluppo nell'adozione di nuove caratteristiche di Windows 8. Attualmente è un program manager del team di Windows Scenario di adozione.