Os Web Services modificaram profundamente a forma como diferentes organizações compartilham informações. O surgimento desta tecnologia tornou viável a transferência de dados entre sistemas em tempo real, com isto acontecendo até mesmo entre aplicações concebidas nas mais diferentes plataformas.

Essa integração só foi possível graças à adoção de padrões para o intercâmbio de dados, merecendo destaque o formato conhecido como SOAP (sigla em inglês para “Simple Object Access Protocol”). Este último nada mais é do que um protocolo XML utilizado em trocas de mensagens entre Web Services e consumidores destes tipos de serviços. Sistemas construídos através das principais plataformas de desenvolvimento da atualidade (.NET, Java, PHP, Delphi etc.) suportam o padrão SOAP, o que comprova a grande aceitação com a qual este formato conta.

Contudo, o protocolo SOAP não deve ser encarado como única alternativa quando da implementação de um novo Web Service. Um segundo padrão para a representação de dados vem ganhando popularidade nos últimos tempos: é a especificação JSON (sigla do inglês “JavaScript Object Notation”).

Originalmente concebida para a manipulação de informações sob a forma de objetos em JavaScript, a notação JSON tem sido também empregada em aplicações convencionais. Uma das vantagens do em relação ao uso do protocolo SOAP está no fato de que JSON permite representar dados de uma maneira mais enxuta (já que dispensa a utilização de numerosas tags, as quais descreveriam a forma como se encontram organizadas as informações em XML).

Sob o ponto de vista estrutural, um objeto JSON é composto por pares de informações; cada par possui um identificador (uma string que define o nome de uma propriedade), além de um valor associado. No que se refere à representação de dados, o padrão JSON permite a representação dos seguintes tipos: Numeric, String (texto iniciado e finalizado por aspas duplas), Boolean (true ou false), Array (os diversos elementos devem estar separados por vírgula e constando dentro de colchetes), Object (diferentes pares de informações dentro de um conjunto de chaves) e null.

Na Listagem 1 é demonstrado um exemplo de objeto contendo dados geográficos de um país (no caso o Brasil), com tais informações já estando em conformidade com o padrão JSON.

Listagem 1. Exemplo de objeto formatado no padrão JSON


  {
     "Pais":"Brasil",
     "Regiao":"América do Sul",
     "Populacao":201032714,
     "PrincipaisCidades":[
        {
           "Nome":"São Paulo",
           "Populacao":11821876
        },
        {
           "Nome":"Rio de Janeiro",
           "Populacao":6323037
        }
     ]
  }

O objetivo deste artigo é demonstrar justamente como dados no formato JSON podem ser convertidos em objetos da plataforma .NET. Isto acontecerá a partir da utilização da classe DataContractJsonSerializer (a qual faz parte do próprio .NET Framework), em uma aplicação ASP.NET Web Forms que consumirá informações sobre exportações e importações geradas por um serviço WCF.

Criando a solução de exemplo

A solução que está sendo apresentada neste artigo foi criada no .NET Framework 4.5, através da utilização do Microsoft Visual Studio 2012 Professional.

Farão parte da Solution TesteJSONxNET dois projetos:

  • TesteJSONxNET.WCF: aplicação WCF em que se encontra definido o serviço BalancoComercialWS, responsável por disponibilizar informações a respeito de importações e exportações por países e continentes, considerando para isto um ano-base;
  • TesteJSONxNET.WebForms: site construído com a tecnologia ASP.NET Web Forms, em que serão exibidos dados fornecidos pelo serviço descrito anteriormente.

Visão geral do Web Service que será empregado nos testes

O projeto TesteJSONxNET.WCF foi baseado em outro artigo que elaborei anteriormente, no qual foi descrita a integração entre serviços WCF e a biblioteca JQuery. Devido a esse motivo e por questões de simplificação, não estarei entrando em maiores detalhes quanto à implementação deste Web Service. A solução contendo este serviço pode ser baixada a partir de um link para download do material relacionado a este post.

O acesso às funcionalidades do serviço BalancoComercialWS acontecerá por meio de URLs, em que serão fornecidos os parâmetros para a recuperação de informações.

Para obter o balanço comercial (exportações/importações) por país, adotar o seguinte padrão (em que “{ano}” corresponde ao período desejado):

http://localhost:5124/BalancoComercialWS.svc/paises/{ano}

Os valores por continente seguem um formato similar, em que o termo “paises” foi substituído por “continentes”:

http://localhost:5124/BalancoComercialWS.svc/continentes/{ano}

OBSERVAÇÃO: as URLs aqui descritas referem-se a um endereço gerado automaticamente pelo Visual Studio. Este caminho poderá variar de uma máquina para outra, já que foi gerado de forma aleatória pela IDE.

Os dados relativos ao balanço comercial para um determinado país estarão no formato JSON, sendo que na Listagem 2 é apresentado um exemplo deste tipo.

Listagem 2. Balanço comercial de um país no formato JSON


  {
     "AnoBase":2011,
     "Pais":"Alemanha",
     "Sigla":"DE",
     "ValorExportado":35.1,
     "ValorImportado":15.5
  }

Já a Listagem 3 demonstra um exemplo similar, considerando neste último caso dados do balanço comercial por continente.

Listagem 3. Balanço comercial de um continente no formato JSON


  {
     "AnoBase":2011,
     "Continente":"América",
     "ValorExportado":66,
     "ValorImportado":30.2
  }

Estes dois exemplos de objetos formatados no padrão JSON servirão de base para a implementação de classes descritas mais adiante.

Procurando simplificar o trabalho de desenvolvedores .NET na construção de tipos utilizados na manipulação de dados no formato JSON, o site json2csharp (http://json2csharp.com/) permite a geração do código que define novas classes a partir de sequências de texto que representem objetos em conformidade com este padrão (Figura 1).

Figura 1. Gerando o código para uma nova classe a partir do site json2csharp

Criando o projeto Web Forms

A aplicação que consumirá os dados providos pelo serviço BalancoComercialWS fará uso da tecnologia ASP.NET Web Forms. Para proceder com a implementação deste site, criar um novo projeto chamado “TesteJSONxNET.WebForms” e que se baseie no template “ASP.NET Web Forms Application” (conforme indicado na Figura 2).

Figura 2. Criando o projeto ASP.NET Web Forms para testes

Criação dos tipos utilizados na manipulação de informações

Com o projeto TesteJSONxNET.WebForms já criado, chega o momento de prosseguir com a construção das classes utilizadas na manipulação das informações de exportação/importação por países e continentes.

Antes de iniciar a codificação dos tipos utilizados na conversão de valores em JSON para objetos .NET, será necessário adicionar uma referência que aponte para a biblioteca System.Runtime.Serialization.dll (conforme indicado na Figura 3). Nesta DLL estão definidos os tipos DataContractAttribute, DataMemberAttribute e DataContractJsonSerializer, cujo uso será descrito em detalhes mais adiante.

Figura 3. Adicionando uma referência que aponte para a biblioteca System.Runtime.Serialization.dll

Primeiramente será implementada a classe ResumoComercialPais (Listagem 4), empregada na exibição dos totais de exportação e importação por país. Este tipo conta com uma estrutura bastante simples, sendo utilizado na conversão de dados do formato JSON (descritos anteriormente na Listagem 2) para uma construção equivalente dentro da plataforma .NET.

Listagem 4. Classe ResumoComercialPais


   using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Web;
  using System.Runtime.Serialization;
   
  namespace TesteJSONxNET.WebForms
  {
      [DataContract]
      public class ResumoComercialPais
      {
          [DataMember]
          public int AnoBase { get; set; }
   
          [DataMember]
          public string Pais { get; set; }
   
          [DataMember]
          public string Sigla { get; set; }
   
          [DataMember]
          public double ValorExportado { get; set; }
   
          [DataMember]
          public double ValorImportado { get; set; }
      }
  }

Já na Listagem 5 está a definição do tipo ResumoComercialContinente. Essa classe está baseada no objeto JSON detalhado na Listagem 3, sendo usada na manipulação das informações comerciais específicas para um determinado continente.

Listagem 5. Classe ResumoComercialContinente


   using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Web;
  using System.Runtime.Serialization;
   
  namespace TesteJSONxNET.WebForms
  {
      [DataContract]
      public class ResumoComercialContinente
      {
          [DataMember]
          public int AnoBase { get; set; }
   
          [DataMember]
          public string Continente { get; set; }
   
          [DataMember]
          public double ValorExportado { get; set; }
   
          [DataMember]
          public double ValorImportado { get; set; }
      }
  }

As classes ResumoComercialPais e ResumoComercialContinente constituem exemplos de estruturas conhecidas como Data Contracts.

Para ser considerado um Data Contract, um tipo precisa ter associado à sua definição um atributo DataContractAttribute (namespace System.Runtime.Serialization). Este último é utilizado para fornecer instruções em modo runtime sobre como a serialização de um objeto ocorrerá (ou mesmo no processo inverso, em que as propriedades de uma instância serão preenchidas com base num conjunto pré-existente de informações – caso justamente do exemplo aqui implementado).

É importante fazer ainda uma observação quanto ao uso da classe DataContractAttribute: por convenção dentro da plataforma .NET, nomes de classes que representam atributos terminam com o sufixo Attribute. Instruções que envolvam um atributo vinculado a uma estrutura de código dispensam o uso de tal sufixo ao final do nome. Logo, ao utilizar o atributo DataContractAttribute, emprega-se apenas “DataContract” na construção a ser marcada com este elemento.

Além da própria definição da classe, as propriedades de um Data Contract que serão utilizadas na conversão de valores JSON para um objeto do .NET Framework precisarão ter um atributo ligado às mesmas: trata-se do tipo DataMemberAttribute (pertencente ao namespace System.Runtime.Serialization), o qual foi associado às diferentes propriedades declaradas nas classes ResumoComercialPais e ResumoComercialContinente.

Implementando a classe responsável pela manipulação de informações no formato JSON

Na Listagem 6 está o código que implementa a classe estática JSONHelper. Esse tipo será empregado na manipulação de informações no padrão JSON, contando com as seguintes operações:

  • GetJSONString: partindo de uma URL que aponta para um serviço remoto, este método retornará uma string em que constarão os dados de um objeto no formato JSON;
  • GetObjectFromJSONString: através de uma string no formato JSON esta operação genérica irá retornar uma instância, a qual é gerada a partir de um tipo fornecido previamente como parâmetro;
  • GetArrayFromJSONString: método bastante similar à operação GetObjectFromJSONString, diferindo apenas pelo fato de retornar um array de objetos. As instâncias geradas por meio deste método também se baseiam em um tipo informado como parâmetro quando este método for acionado.

Quanto à forma como o estático método GetJSONString foi implementado, é necessário destacar:

  • Utilizando uma URL fornecida como parâmetro de entrada, esta operação gera uma nova instância da classe HttpWebRequest (namespace System.Net), invocando para isto o método Create definido no tipo WebRequest (namespace System.Net). A classe HttpWebRequest deriva do tipo abstrato WebRequest, sendo empregada no envio de requisições HTTP direcionadas a uma URL específica;
  • Partindo da instância da classe HttpWebRequest gerada no passo anterior, é realizada uma chamada ao método GetReponse. O retorno desta ação será uma instância do tipo WebResponse (namespace System.Net), a qual corresponde à resposta produzida ao se invocar a URL fornecida como parâmetro à operação GetJSONString;
  • Acessando a operação GetResponseStream através da referência do tipo WebResponse associada à variável “response”, será obtida uma instância da classe Stream (namespace System.IO). Em termos práticos, esta instância do tipo Stream nada mais é do que uma sequência com os dados retornados após o envio de uma chamada a um Web Service;
  • Por fim, é gerada uma instância do tipo StreamReader (namespace System.IO), fornecendo-se ao construtor da mesma a referência da classe Stream obtida no passo anterior, além do valor de enumeration Encoding.UTF8 (a classe Encoding faz parte do namespace System.Text). UTF-8 é um padrão para representação de caracteres, sendo a alternativa deste tipo mais utilizada na Internet atualmente; possuindo total compatibilidade com a especificação ASCII, este formato suporta ainda alfabetos como grego, cirílico, hebreu e árabe. A invocação do método ReadToEnd sobre o objeto StreamReader produzirá como resultado uma string, que será devolvida como resultado da execução da operação GetJSONString.

Sobre a implementação do método GetObjectFromJSONString, é possível observar:

  • A presença da declaração “where T: new()”, a qual representa um exemplo de uso de um recurso de Generics conhecido como constraint. Graças a este mecanismo é possível estipular condições que devam ser atendidas pelo tipo T. Considerando especificamente a instrução “T: new()”, esta restrição indica que somente classes que contem com um construtor sem parâmetros (implementado explicitamente ou não) poderão ser indicadas no lugar do placeholder <T>;
  • A geração de um objeto (variável “stream”) baseado no tipo MemoryStream (namespace System.IO), de forma a possibilitar a conversão da string contendo os dados no formato JSON em uma instância do tipo T. Esta referência da classe MemoryStream recebe como parâmetro um array de bytes, que corresponde aos dados JSON no formato UTF-8 (empregando para isto isto o método GetBytes da propriedade UTF8, a qual faz parte da classe Encoding);
  • Finalmente, uma instância da classe DataContractJsonSerializer (namespace System.Runtime.Serialization.Json) é criada, tomando como base o tipo associado ao placeholder “T”. A invocação do método ReadObject (a partir desta última referência gerada) efetuará a conversão dos dados no formato JSON para um novo objeto do tipo T, com tal instância sendo o resultado da execução da operação GetObjectFromJSONString.

Já em termos estruturais, a operação GetArrayFromJSONString é quase idêntica ao método GetObjectFromJSONString, diferindo deste último apenas por retornar um array de instâncias baseadas no tipo genérico T.

Listagem 6. Classe JSONHelper


  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Web;
  using System.Net;
  using System.IO;
  using System.Text;
  using System.Runtime.Serialization.Json;
   
  namespace TesteJSONxNET.WebForms
  {
      public static class JSONHelper
      {
          public static string GetJSONString(string url)
          {
              HttpWebRequest request =
                  (HttpWebRequest)WebRequest.Create(url);
              WebResponse response = request.GetResponse();
   
              using (Stream stream = response.GetResponseStream())
              {
                  StreamReader reader = new StreamReader(
                      stream, Encoding.UTF8);
                  return reader.ReadToEnd();
              }
          }
   
          public static T GetObjectFromJSONString<T>(
              string json) where T : new()
          {
              using (MemoryStream stream = new MemoryStream(
                  Encoding.UTF8.GetBytes(json)))
              {
                  DataContractJsonSerializer serializer =
                      new DataContractJsonSerializer(typeof(T));
                  return (T)serializer.ReadObject(stream);
              }
          }
   
          public static T[] GetArrayFromJSONString<T>(
              string json) where T: new()
          {
              using (MemoryStream stream = new MemoryStream(
                  Encoding.UTF8.GetBytes(json)))
              {
                  DataContractJsonSerializer serializer =
                      new DataContractJsonSerializer(typeof(T[]));
                  return (T[])serializer.ReadObject(stream);
              }
          }
      }
  }

Implementando a funcionalidade para consulta de informações no formato JSON

Antes de prosseguir com a implementação da funcionalidade para consulta ao balanço comercial de países e continentes em um ano específico, será necessário adicionar dois itens no elemento appSettings do arquivo Web.config (conforme indicado na Listagem 7):

  • UrlBalancoComercialPaises endereço-base para acesso às informações de países num determinado ano;
  • UrlBalancoComercialContinentes: URL que possibilitará a obtenção dos dados com os totais de importação e exportação por continentes dentro do período de um ano.

Listagem 7. Ajustes no arquivo Web.config da aplicação TesteJSONxNET.WebForms


   <?xml version="1.0" encoding="utf-8"?>
  <configuration>
     ...
     <appSettings>
      <add key="UrlBalancoComercialPaises"
         value="http://localhost:5124/BalancoComercialWS.svc
         /paises/{0}" />
      <add key="UrlBalancoComercialContinentes"
        value="http://localhost:5124/BalancoComercialWS.svc
         /continentes/{0}" />
    </appSettings>    
   
    ...
   </configuration>

Adicionar ao formulário Default.aspx (gerado automaticamente ao se criar o projeto TesteJSONxNET.WebForms) os controles listados na Tabela 1. Será nesta página em que acontecerá a exibição dos valores disponibilizados pelo serviço BalancoComercialWS, considerando para isto a conversão dos dados no formato JSON para objetos do .NET Framework.

Tipo do Controle

Nome do Controle

DropDownList

ddlAnoBase

Panel

pnlBalancoComercial

Label

lblBalancoContinentesJSON

GridView

grdBalancoContinentes

Label

lblBalancoPaisesJSON

GridView

grdBalancoPaises

Tabela 1. Controles a serem adicionados à página Default.aspx

Os controles “lblBalancoContinentesJSON”, “grdBalancoContinentes”, “lblBalancoPaisesJSON” e “grdBalancoPaises” estarão localizados dentro do componente “pnlBalancoComercial”. Este painel será utilizado para se determinar a exibição ou não de informações (com base nos valores selecionados do DropDownList “ddlAnoBase”).

OBSERVAÇÃO: para efeitos de simplificação, não será detalhado o código contendo os diferentes controles e elementos HTML da página Default.aspx. O arquivo com todos estes detalhes poderá ser obtido a partir do material para download deste artigo.

Na Listagem 8 está a implementação da página Default.aspx. Foram definidos nesta página os seguintes eventos:

  • Page_Load: caso o acesso a uma página não corresponda a um PostBack (uma atualização com a página já carregada), o painel “pnlBalancoComercial” será ocultado, obrigando um usuário a selecionar um ano para a consulta de informações;
  • ddlAnoBase_SelectedIndexChanged: este método será executado como resposta ao evento OnSelectedIndexChanged do controle “ddlAnoBase”, ocorrendo quando o usuário selecionar um item neste componente. Caso o resultado desta seleção seja um ano válido, o método GetJSONString será acionado duas vezes, recebendo como parâmetro os endereços para a obtenção do balanço comercial por país e por continente para um determinado período (tais URLs constam no arquivo Web.config da aplicação, podendo ser acessadas através da classe ConfigurationManager – namespace System.Configuration). Os resultados da execução do método GetJSONString serão exibidos nos Labels (no formato JSON) e GridViews (como objetos do .NET Framework, convertidos através do método GetArrayFromJSONString) que estão localizados dentro do controle “pnlBalancoComercial”.

Listagem 8. Implementação da página Default.aspx


   using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Web;
  using System.Web.UI;
  using System.Web.UI.WebControls;
  using System.Configuration;
   
  namespace TesteJSONxNET.WebForms
  {
      public partial class _Default : Page
      {
          protected void Page_Load(object sender, EventArgs e)
          {
              if (!Page.IsPostBack)
                  pnlBalancoComercial.Visible = false;
          }
   
          protected void ddlAnoBase_SelectedIndexChanged(
              object sender,
              EventArgs e)
          {
              if (ddlAnoBase.SelectedIndex > 0)
              {
                  pnlBalancoComercial.Visible = true;
   
                  string strJSON;
   
                  strJSON = JSONHelper.GetJSONString(String.Format(
                      ConfigurationManager.AppSettings[
                          "UrlBalancoComercialContinentes"],
                      ddlAnoBase.SelectedValue));
                  lblBalancoContinentesJSON.Text = strJSON;
                  grdBalancoContinentes.DataSource = JSONHelper
                    .GetArrayFromJSONString<ResumoComercialContinente>(
                          strJSON);
                  grdBalancoContinentes.DataBind();
   
                  strJSON = JSONHelper.GetJSONString(String.Format(
                      ConfigurationManager.AppSettings[
                          "UrlBalancoComercialPaises"],
                      ddlAnoBase.SelectedValue));
                  lblBalancoPaisesJSON.Text = strJSON;
                  grdBalancoPaises.DataSource = JSONHelper
                      .GetArrayFromJSONString<ResumoComercialPais>(
                          strJSON);
                  grdBalancoPaises.DataBind();
              }
              else
              {
                  pnlBalancoComercial.Visible = false;
              }
          }
      }
  }

Executando a aplicação de testes

Na Figura 4 é apresentada a tela inicial que aparecerá ao se executar a aplicação TesteJSONxNET.WebForms.

Figura 4. Executando a aplicação TesteJSONxNET.WebForms

Selecionando o ano de 2011 serão exibidos os dados por continentes (Figura 5) e países (Figura 6) para este período.

Figura 5. Balanço comercial por continentes no ano de 2011

Figura 6. Balanço comercial por países no ano de 2011

O uso do padrão JSON para a representação de dados vem crescendo de forma contínua, observando-se a adoção deste formato para a transferência de informações em aplicações dos mais variados tipos. Esta tendência tem sido favorecida tanto pelo sucesso de frameworks como JQuery (e que empregam extensivamente JSON para a manipulação de informações), quanto pela adoção de modelos como REST para a implementação de soluções orientadas a serviços (priorizando, neste último caso, uma arquitetura mais simples e que foque numa maior performance na comunicação entre sistemas).

Conforme descrito neste artigo, o consumo de dados no formato JSON em .NET não costuma exigir grandes esforços. Isto é possível graças à classe DataContractJsonSerializer, a qual existe desde a versão 3.5 do .NET Framework. Este tipo foi criado, basicamente, com a finalidade de facilitar a implementação de funcionalidades que envolvam a serialização de dados no padrão JSON. Importante ressaltar que o processo inverso (no qual instâncias seriam geradas a partir de uma string neste formato) também é suportado pela classe DataContractJsonSerializer.

Espero que o conteúdo aqui apresentado possa lhe auxiliar no dia-a-dia. Até uma próxima oportunidade!

Links

DataContractJsonSerializer Class

http://msdn.microsoft.com/en-us/library/system.runtime.serialization.json.datacontractjsonserializer(v=vs.110).aspx

WCF x REST: uma alternativa para serviços

//www.devmedia.com.br/wcf-x-rest-uma-alternativa-para-servicos/28426