REST não é JSON

Atualmente, é muito comum ouvirmos falar sobre APIs REST (REpresentational State Transfer). APIs REST são anunciadas como uma alternativa com maior interoperabilidade e facilidade de uso. O formato JSON (JavaScript Object Notation) de serialização de dados é comumente associado à APIs REST, de modo que ambos os conceitos se confundem na cabeça das pessoas.  Esta postagem pretende esclarecer as diferenças entre ambos os conceitos e como eles estão relacionados, facilitando o uso e construção de APIs REST.

REST ? JSON ? O que são ? Onde vivem ? De que se alimentam ?

JSON

JSON é um formato para transferência de dados entre programas. Foi primeiramente especificado e popularizado por Douglas Crockford — um evangelista do Javascript e atualmente engenheiro sênior no Paypal — no início dos anos 2000.

Documentos JSON têm diversas vantagens entre as quais encontra-se a legibilidade (seres humanos conseguem ler facilmente um documento JSON). Mensagens em JSON também são menores que mensagens equivalentes codificadas em outros formatos (olá XML !!!!). Uma outra vantagem importante é que, graças a popularidade do formato, navegadores costumam ter seus parsers de JSON escritos de modo nativo. O que acarreta melhor desempenho.

REST

Em 2000,  Roy Field definiu o modelo REST, em sua tese de doutorado chamada Architectural Styles and the Design of Network-based Software Architectures. Sua principal motivação foi a descrição de como uma aplicação web — ou API —  deve se comportar. A justificativa fora que, seguindo estas recomendações, aplicações apresentariam requisitos não-funcionais importantes, como  escalabilidade, modificabilidade, confiabilidade, entre outros.

O elemento primordial de uma API Rest é o recurso.  Pode-se traçar um paralelo entre recursos e objetos, do paradigma OO (Orientado a Objetos): ambos representam conceitos concretos e abstratos (i.e. coisas) em um sistema; e, ambos são compostos por atributos, associações e métodos. Porém, ao contrário de objetos, recursos têm seus métodos previamente definidos pelo protocolo HTTP.  

A identificação de um recurso dentro de um sistema se dá através de URIs (Uniform Resource Identifier — identificador uniforme de Recursos). O padrão de codificação dos dados trafegados é definido através de cabeçalhos na requisição e na resposta (Accept e Content-Type).

Os mecanismos de identificação e codificação aliados aos métodos do protocolo HTTP definem uma interface uniforme para acesso (e manipulação) de recursos. Notavelmente, não existe nenhuma regra que dita que APIs Rest devam usar JSON como meio de transferência de dados. JSON é apenas uma das possíveis formas de transferência. É perfeitamente legal, e interessante, do ponto de vista de um vendor, o suporte a diferentes formas de transferência de dados (JSON, XML, YAML) em uma mesma API.

Tá, mas e daí ?

Conforme discorrido, um dos pilares de uma API RESTful é a uniformidade das interfaces. Bibliotecas e frameworks dependem dessa semântica para operar de maneira correta. A integração se torna problemática quando o desenvolvedor de uma API decide não adotar a semântica esperada.

Existem algumas características cuja presença normalmente indica equívocos na adoção do paradigma REST. Entre elas, pode-se citar a presença de verbos nas URLs e a falha na adoção dos códigos de erros definidos pelo protocolo HTTP.

Presença de verbos nas URL

A presença de verbos nas URL (e.g. http://minha.api.com.br/usuarios/45/bloquear) indica que o desenvolvedor não adotou a recomendação na qual uma URL identifica um recurso. É provável também que os verbos HTTP não foram utilizados da maneira correta e suas características básicas (idempotência, cacheabilidade e etc.) não foram honradas.

Uma modelagem mais adequada consiste na criação de um recurso bloqueio, aninhado ao usuário (http://minha.api.com.br/usuarios/45/bloqueio). Criações de bloqueios seriam realizadas através de requisições POST à URL (que retornariam código 201 caso a criação fosse bem-sucedida). Verificação de bloqueios seriam realizadas através de GETs. Os códigos de retorno (200 e 404, respectivamente) indicariam se o usuário está bloqueado ou não. Desbloqueios se resumiriam a requisições DELETE que retornariam o código 204 (ou 200, caso seja necessário o retorno de alguma informação extra) caso fossem bem sucedidas.

Falha na adoção de códigos de erro do protocolo HTTP

Outro mau-cheiro comumente encontrado reside no tratamento de erros. De maneira análoga aos verbos, o protocolo HTTP provê uma gama de erros com semântica pré-definida que são amplamente usados por frameworks e bibliotecas de integração.

Um exemplo é o Ember Data, componente do Ember.js que implementa repositórios de dados, conforme definido pela abordagem DDD (Domain Driven Design). Buscas no backend que retornem o código 404 são interpretadas como ausência do recurso e transparentemente tratadas pelo framework. Já buscas que retornem código 200 são tratadas como entidades válidas e retornadas ao código cliente (código consumindo a API).

A grosso modo, não existe como um framework distinguir entre um recurso sem nenhuma propriedade e a ausência de um recurso.  Logo, o desenvolvedor é obrigado a escrever código de tratamento de erro mais rebuscado e deixa de aproveitar os recursos de tratamento de erro do framework. Além disso, um framework pode adotar estratégias de cacheamento no intuito de diminuir o número de requisições (e.g. entidades podem ser buscadas uma única vez e seu conteúdo armazenado localmente por um tempo estabelecido). Neste caso, corre-se o risco de se cachear uma entidade vazia.

Algumas vezes a semântica de um código de um erro não é insuficiente para desenhar um panorama de sua causa. O erro 400 (BAD_REQUEST) é normalmente utilizado para indicar problemas de validação da entrada de um serviço (uma data inválida, por exemplo). Nesse caso, indica-se o enriquecimento da respostas de erros (no exemplo anterior, poderia-se enviar uma lista contendo os campos inválidos e uma breve explanação das razões para sua invalidez).

Conclusão

Apesar de serem conceitos distintos, muitas vezes não fica claro para o desenvolvedor o que caracteriza uma API como REST. Comumente APIs são anunciadas como APIs REST APIs que não seguem este paradigma. Estas APIs seguem o modelo RPC tradicional e usam JSON para a transferência de dados. É importante que tenhamos em mente o real significado destes conceitos. Caso contrário, corre-se o risco de gerarmos expectativas erradas sobre o funcionamento das coisas. O que por fim gera retrabalho e insatisfação da equipe e do cliente.

P.S. Veja também o post Boas Práticas para Desenvolvimento de APIs REST, onde são descritas alguns padrões para a escrita de API Rest.

 

Iniciando com o POSTMAN

Atualmente no desenvolvimento de software temos que evoluir nossos produtos saindo do modelo monolítico, e caminharmos cada vez mais para o caminho dos micros serviços, onde são pequenos módulos do sistema espalhados e dinâmicos.

Neste cenário temos várias integrações entre os módulos e uma camada de cliente, sendo Mobile, Site ou um outro sistema. Normalmente para acesso a cada módulo é disponibilizado uma Application Programming Interface (API) que é um conjunto de rotinas e padrões estabelecidos por um software para a utilização das suas funcionalidades por aplicativos que não pretendem envolver-se em detalhes da implementação do software, mas apenas usar seus serviços disponibilizados.

Para ter uma melhor dinâmica e entendimento desta comunicação normalmente disponibilizamos uma API na qual ela é acompanhada de uma documentação de como é a camada de comunicação, e os respectivos dados necessários. Grande parte das documentações são disponibilizados nos padrões do swagger.io onde é um formato de documentação para API’s rest caso queria saber mais acesse este POST da MATERA: Clique aqui para acessar o post.

E com essa documentação, vem o POSTMAN, na qual, é possível realizar as requisições conforme as especificações, sem termos implementado ao menos uma linha de código, desta forma, auxiliando nos testes de  API’s disponibilizadas por terceiros, e para que possamos também realizar testes em nossas API’s para verificar se o comportamento dela está dentro do esperado inclusive é possível realizar exportações destes testes para incluirmos essa execução em software de integração contínua para garantirmos a maior qualidade das Api’s disponibilizadas.

Iniciaremos com o processo de instalação do POSTMAN, alguns questionamentos que podem aparecer no ato da instalação.

  • Preciso ter o Chrome para conseguir instalar?

Não atualmente o POSTMAN conta com instalações nativas para Mac, Windows e Linux, link onde é possivel realizar o download https://www.getpostman.com/

  • Qual a vantagem de instalar o POSTMAN nativo ao invés do complemento do Chrome?

Ele conta com recursos que não são disponíveis pelo complemento do Chrome:

  • Cookies – permitem que você trabalhe com cookies diretamente;
  • Built-in proxy – vêm com um proxy interno que você pode usar para capturar o tráfego da rede;
  • Menu bar – uma barra de menu mais completa, onde é possível criar coleções, alterar para o histórico de requisições entre outras funcionalidades;
  • Restricted headers – é possível enviar nos headers da requisição Origin e User-Agent personalizados;
  • POSTMAN Console – tem um console embutido, que permite exibir os detalhes do pedido de rede para chamadas de API.

Algumas destas vantagens como o POSTMAN console é disponível para o aplicativo/extensão do chrome porém para isso é necessário a instalação de um outro plugin, e ainda realizar configurações para liberação do console para aplicativos/complementos do chrome.

Após a instalação do POSTMAN a tela inicial/usabilidade do POSTMAN será mostrada como na Figura 1:

postman_tela_inicial

 Figura 1 – Tela Inicial Postman

Como realizar minha primeira operação, podemos observar na Figura 2:

postman_get

 Figura 2 – Menu de requisições

No campo GET, é possível mudar o tipo da requisição que será feita para a URL que será passada no campo posterior, tendo várias operações como POST, DELETE, PUT, OPTIONS, entre outras.

Antes do botão salvar no params, é possível informar parâmetros que serão passados no formato de requisições get sendo url?param1=valor1&param2=valor2, porém tendo um preenchimento mais amigável.

Abaixo temos 5 guias sendo elas:

  • Authorization : onde informamos o tipo de autenticação necessária e como será feita a obtenção desse token para que possa passar para a API
  • Headers : onde passamos todos os cabeçalhos da request, ele contém um auto-complete nas chaves.
  • Body: aqui colocamos o corpo da request, quando é uma requisição que tenha corpo como por exemplo um POST, informamos o tipo da informação e o seu corpo, caso seja XML, JSON, HTML,Text selecionar o tipo raw, que será acionado uma compo para que consiga passar o body corretamente.
  • Pre-request Script: São comandos em JS que serão executados, antes da requisição.
  • Tests: são validações para que realize teste na request do valor retornado e manipulação de dados.

 Ao final das abas temos 2 termos em laranja sendo eles:

  • Cookies – onde iremos manipular caso necessário o cookie da request
  • Code – onde o POSTMAN tem um gerador de código fonte em algumas linguagens referente a esta request que você está criando.

 Para criamos algumas requisições válidas vamos utilizar os exemplos que estão junto com a instalação do POSTMAN onde contém algumas requisições de exemplo para verificar o comportamento do software conforme Figura 3 :

postman_collection

 Figura 3 – Painel de requisições de exemplo POSTMAN

Para iniciarmos com o POSTMAN vamos usar a requisição “Request Headers” da própria coletânea do POSTMAN , e adicionarmos o nosso header, conforme Figura 4. postman_headers

 Figura 4 – Painel headers da requisição

A resposta do serviço do POSTMAN irá nos retornar um json com atributo headers, e tendo uma propriedade com nosso header e valor. Também é possível observar na parte superior da resposta uma barra onde contém todas as informações referente a este request, conforme Figura 5.postman_result_header

 Figura 5 – Response de exemplo da requisição header

Agora iremos fazer uma requisição “POST” utilizando o exemplo “POST Request”, nesta requisição iremos alterar na guia body, o corpo da requisição que desejamos enviar e avaliar o seu retorno, conforme Figura 6.postman_body

 Figura 6 – Painel de alteração do body da requisição

Na resposta, temo como foi enviado a informação para o serviço e depois uma propriedade com o tipo de dado da requisição e as informações enviadas, conforme Figura 6.postman_post_test

  Figura 7 – Response de exemplo da requisição de alteração do body

Com este post é possível, ter o conhecimento inicial do POSTMAN, e como fazer a comunicação dele com suas api’s ou de terceiros. Nos próximos post’s iremos nos aprofundar mais sobre essa ferramenta, criando collections, testes automáticos, e ainda iremos utilizarmos recursos npm para executar os testes em nossos serviços, assim podendo integrar em uma ferramenta de integração contínua.

 

Referências

[1] https://www.getpostman.com/

[2] http://swagger.io/

 

Acessando api REST com Pentaho DI

Olá!

No post “Acessando web services com pentaho DI” expliquei como consultar informações vindas de um web service SOAP utilizando o pentaho DI. Nesse post, vamos criar uma transformação parecida, mas consultando um serviço REST que retorna um JSON. Caso você não esteja familiarizado com a arquitetura REST, recomendo que dê uma olhadinha no post “REST usa JSON e SOAP usa XML, certo?“, aqui mesmo no blog da MATERA.

 

Descrição do cenário

Nesse projeto, vamos acessar um serviço REST para consulta de endereço, dado um CEP, mas dessa vez, a api não será dos correios, e sim um projeto open source disponível nesse link. Nessa api, devemos realizar uma chamada GET informando na própria URL o CEP do endereço desejado. A resposta será um JSON com os dados de logradouro, bairro, cidade, estado etc. Vamos comecar!!

 

Montando a transformação

1 – Vamos começar criando uma nova transformação. Clique no menu “File -> Novo -> Transformação“, ou aperte “Ctrl + N“.

Figura 1 - Nova transformação
Figura 1 – Nova transformação

 

2 – Inclua um componente “Data Grid na área de trabalho da transformação. Para isso, clique na aba “Design no canto superior esquerdo da janela e localize a pasta “Input ou digite no campo “steps o nome “Data Grid“. Arraste o componente para a área de trabalho da transformação(vamos utilizar o “data grid” para incluir dados estáticos para os testes. Em um cenário real, essas informações poderiam vir de um banco de dados, por exemplo).

Figura 2 - Incluindo um data grid
Figura 2 – Incluindo um data grid

 

3 – O componente data grid permite definir um conjunto de colunas e um conjunto de dados para as colunas definidas. Isso será útil para simplificar nossos testes. Dê dois cliques no componente “Data Grid“. Na janela que se abre, inclua uma coluna chamada “cepOrigem” do tipo “String na aba “Meta. Na aba “Data“, define alguns CEPs para realizar a consulta. Aproveite para definir um nome para o componente. Defina algo que indique o que o componente faz, conforme figura 3.

Figura 3 - Componente Data Grid
Figura 3 – Componente Data Grid

 

4 – Inclua agora, na área de trabalho da transformação, um componente “Add constants“, disponível na pasta “Transform“. Como o próprio nome diz, esse componente permite definir um campo com valor fixo. Vamos definir nele a URL do serviço. Dê dois cliques no componente e inclua um campo url do tipo “String“, e defina o valor do campo como “http://correiosapi.apphb.com/cep/“. Aproveite para definir um nome para o componente, conforme figura 4.

Figura 4 - Componente Add Constants
Figura 4 – Componente Add Constants

 

5 – Vamos agora incluir um componente “Calculator“. Esse componente é muito útil, pois com ele é possível realizar operações matemáticas com até 3 campos. O mais interessante é que podemos realizar operações não só com valores numéricos, mas com “Strings” também, isso significa que uma String A + String B resultará na concatenação dos campos A e B. Vamos fazer isso para montar a URL, pois precisamos incluir o CEP do endereço no fim da URL base, mas antes, vamos ligar um elemento ao outro, conforme figura 5. Cuidado, a flecha indica a ordem em que os dados serão tratados, não inverta a ordem.

Figura 5 - Associando componentes
Figura 5 – Associando componentes

 

6 – Agora sim vamos configurar a calculadora. Dê dois cliques no componente “Calculator” e defina um nome para o campo resultante da operação, o cálculo que será executado, no nosso caso “A + B” e os campos envolvidos na operação, no nosso caso, “url” e “cepOrigem”. Aproveite para definir um nome explicativo para o componente (Figura 6).

Figura 6 - Componente Calculadora
Figura 6 – Componente Calculadora

 

7 – Finalmente, vamos incluir na transformação o componente “REST Client“. Associe o componente “Calculator” com o componente recém adicionado e dê dois cliques nele. Na janela que se abre, clique na opção “Accept URL from field?” e selecione o campo da url completa, criada no componente “Calculator“. Deixe a opção “Get Method from field” como “GET“, mude a opção “Application type” para “JSON” e defina um nome para o resultado da consulta na opção “Result field name“. Não esqueça de definir um nome explicativo para o componente.

Figura 7 - Componente REST Client
Figura 7 – Componente REST Client

 

8 – Precisamos agora tratar a resposta da consulta. Inclua um componente “Json Input” na transformação, associe-o ao componente “REST Client” e dê dois cliques para configurá-lo. Na aba “File“, marque a opção “Source is defined in a field?” e selecione o campo de retorno do componente REST na opção “Get source from field“, conforme figura 8.

Figura 8 - Componente Json Input
Figura 8 – Componente Json Input

 

9 – Ainda no componente “Json Input“, clique na aba “Fields“. Aqui vamos criar uma variável para cada atributo de retorno do JSON. Para isso precisaremos indicar os campos seguindo o padrão “JSONPath” descrito nesse link. O retorno da consulta REST é bem simples, por isso, vamos apenas utilizar a notação “$.nomeAtributo” para buscar o atributo diretamente descendente da raiz. Ficará como na figura 9.

Figura 9 - Recuperando dados do Json
Figura 9 – Recuperando dados do Json

 

10 – Agora só precisamos exibir os resultados. Se fosse uma aplicação, poderíamos enviar os dados para um banco de dados ou uma planilha, ou até mesmo incluir outros passos no pentaho para modificar esses dados. No nosso caso vamos apenas exibir os dados retornados no próprio log do pentaho. Inclua o elemento “Write to log“, associe o elemento “Json Input” a ele e dê dois cliques. No campo “Fields“, selecione as variáveis definidas anteriormente no “Json Input“. Não esqueça de colocar um nome para o componente.

Figura 10 - Configurado log
Figura 10 – Configurado log

 

Agora só rodar e ver o resultado. Salve a transformação e clique no botão “Run” ou aperte “F9“. Para visualizar os dados, clique na aba “log” na janela que se abre após o início do processamento, na parte inferior do pentaho.

Figura 11 - Resultado da consulta
Figura 11 – Resultado da consulta

 

Nesse post mostramos que a consulta e manipulação de informações vindas de uma API REST torna-se bastante visual e prática utilizando os componentes “Rest Client” e “Json Input“. Aproveitamos também para explorar outros componentes de manipulação de informações do pentaho DI, como o “Calculator” e “Write to log“. Tente alterar essa transformação mudando o componente de entrada para que busque os ceps de um banco de dados ou de uma planilha, e a saída para um arquivo texto, por exemplo. Explore os diversos componentes que o pentaho DI oferece e aproveite para olhar também os exemplos que vem com o próprio aplicativo. Dessa forma você terá uma visão dos benefícios e limitações dessa ferramenta fantástica.

Até a próxima!

 

REST usa JSON e SOAP usa XML, certo?

SOAP (Simple Object Access Protocol) e REST (Representation State Transfer) são duas arquiteturas diferentes de web services. Entretanto, um erro comum é achar que a principal diferença entre elas é que SOAP utiliza XML (eXtensible Markup Language) e REST utiliza exclusivamente JSON (JavaScript Object Notation). Por esta razão, este artigo apresenta os conceitos básicos destas duas tecnologias, visando um maior esclarecimento das suas características e diferenças.

Em uma breve descrição, SOAP é uma tecnologia desenvolvida pela Microsoft para acessar web services unicamente por meio de mensagens de requisições e respostas feitas em XML, substituindo as tecnologias que utilizavam mensagens binárias. Além das mensagens, utiliza-se XML para criar o arquivo WSDL (Web Service Description Language), que é um contrato entre o provedor e o consumidor do serviço, como se fosse uma assinatura de método para o serviço web. Outra característica do SOAP é que é independente do protocolo de transporte, ou seja, pode ser enviado com a maioria dos protocolos, por exemplo HTTP, SMTP, TCP e JMS.

Já o REST é uma arquitetura criada para ser mais simples de se usar que o SOAP. Este pode ser usado em vários formatos de texto, como CSV (Comma-separated Values), RSS (Really Simple Syndication), JSON e YAML. Porém, só pode ser utilizado com o protocolo HTTP/HTTPS, por exemplo utilizando os métodos GET, POST, PUT e DELETE. Por este motivo, o REST comporta-se como se fosse um navegador que sabe como usar um protocolo e seus métodos padronizados, sendo que o aplicativo deve se adequar a isto. Neste caso, os padrões do protocolo não são violados, por exemplo criando métodos extras, aproveitando assim os métodos nativos e criando as ações com eles em seu tipo de mídia.

A seguir, temos uma comparação das duas tecnologias, com as principais vantagens de cada uma:

SOAP

  • Protocolo de transporte independente (REST utiliza somente HTTP)
  • Trabalha melhor com sistemas distribuídos, pois REST trabalha com comunicação ponto-a-ponto
  • O arquivo WSDL pode gerar um certo tipo de automação quando usado com determinadas ferramentas

REST

  • Melhor curva de aprendizado
  • Mensagens menores e mais eficientes como o formato JSON comparado com XML
  • Os dados podem ser colocados em cache, retornando sempre a mesma resposta para a mesma requisição
  • Mais rápido pois precisa de menos processamento que o SOAP

Portanto, vimos que cada arquitetura possui vantagens e desvantagens e é preciso analisar caso a caso antes de escolher qual utilizar. Porém, por ser mais simples e utilizar um protocolo que já é vastamente adotado pela comunidade, o REST geralmente é a arquitetura mais indicada para implementar um web service. Além do mais, com REST podemos trabalhar com diferentes formatos de texto enquanto SOAP é limitado exclusivamente ao formato XML.

Referências

(Java EE 7 – JSR353) Trabalhando com objetos JSON em aplicações Java

JSON

Olá pessoal, neste post vamos falar um pouco a respeito da API padrão para processamento de objetos JSON (Javascript Object Notation), a “Java API for JSON Processing” ou JSON-P, disponibilizada através da JSR353 como parte da especificação do Java EE 7.

O que são as JSRs

Para quem não conhece as JSRs (Java Specification Requests, ou Requisição de especificação Java) são documentos construídos com intuito de formalizar alguma funcionalidade ou nova tecnologia que fará parte da plataforma Java, em um sentido geral. Estas especificações são definidas através de um processo, denominado “Java Community Process” ou “JCP”.

Por meio do JCP a especificação é disponibilizada para toda a comunidade, e os desenvolvedores e demais interessados que desejarem podem participar de perto deste processo, e acompanhar toda a evolução de uma determinada JSR por meio do site do JCP, pois toda a evolução de uma JSR é dsponibilizada em forma de revisões públicas, onde os membros da comunidade Java podem acompanhar a evolução a respeito da nova tecnologia ou funcionalidade foco da JSR.

Assim que uma JSR chega ao seu estado final, ela passa a ser considerada a implementação de referência de uma determinada tecnologia, assim quando falamos que algumas JSRs foram disponibilizadas em uma versão específica do Java Enterprise Edition, em termos gerais estamos dizendo que novas funcionalidades e melhorias estão disponíveis agora nesta versão da plataforma, e podem ser utilizadas no desenvolvimento de novas aplicações.

Sobre as demais “JSRs” que compõem o Java EE 7, há um link no final da página com maiores detalhes a respeito de cada uma delas, para que o leitor que desejar possa conferir.

Falando a respeito da manipulação de informações no formato JSON, até a versão 6 do JAVA EE, não havia um padrão definido pela especificação Java com este fim, assim as aplicações desenvolvidas que tinham a necessidade de processar informações neste formato, utilizavam ferramentas próprias ou mesmo desenvolvidas por terceiros, que normalmente não seguem um padrão em comum de se trabalhar entre elas, tendo cada uma delas a sua própria forma de trabalhar com as informações, a respeito destas ferramentas podemos citar alguns exemplos como “Jackson”, ou Google G-SON.

Com a definição de um padrão para atender esta necesssidade, as aplicações passam a contar com uma maior portabilidade entre diferentes servidores de aplicação existentes, e passam a necessitar menos de ferramentas próprias ou de terceiros, o que contribui para diminuir a complexidade do código gerado, pois a nova especificação já traz ao desenvolvedor um conjunto de funcionalidades implementadas para este fim.

O formato JSON

Com o crescente aumento das tecnologias móveis, e utilização de serviços WEB, o formato JSON tem sido cada dia mais utilizado para troca de informações entre diferentes aplicações.

O formato JSON é bastante limpo, claro e fácil de manipular, sendo o formato mais comumente utilizado por serviços do tipo “RESTful”.

Outros exemplos da utilidade do formato JSON, incluem:

  •  criação de arquivos de configuração, devido sua fácil leitura e manutenção
  • utilizado em requisições do tipo “ajax” para troca de informações
  • utilizado em bancos de dados conhecidos como “NoSQL”

No formato JSON podemos representar as informações de duas formas, podendo ser:

  1. Objetos:
    São representadas por sequências não ordenadas de pares de “chave” e “valor” separados por “:”, contidas pelo uso do “{” e “}”, sendo que cada par “chave/valor” é separado por “,”, também utilizado para separação de objetos. Cada chave é representada por uma “string”, e possui um “valor” associado a ela (descrição abaixo).
  2. Arrays:
    São representados por uma sequência ordenada de “valores”, separados entre si por “,”, contidos pelo uso do “[” e “]”.

Quanto aos valores utilizados em um Objeto ou Array JSON, podem ser dos tipos “string”, “numero”, “objeto”, “array”, “true”, “false” ou “null”.

Vale ressaltar que o formato JSON possibilita o uso de “valores” como sendo outros Objetos JSON, possibilitando a construção de hierarquias dentre os diferentes objetos associados.

Como exemplo, vamos imaginar que estejamos desenvolvendo uma aplicação, que recebe dados de uma loja de livros, e através do formato JSON as informações dos livros a venda são disponibilizadas a nossa aplicação, a seguir temos um exemplo de como poderia ser informado um determinado “livro”, disponibilizado para nós no formato JSON:

{ "id":98746,
  "nome":"Artificial Intelligence",
  "subtitulo":"A Modern Approach",
  "preco":56.84,
  "isbn":{"tipo":13,
          "valor":"978-0136042594"},
  "editora":"Prentice Hall",
  "edicao":3.0,
  "ano":2009.0,
  "autor":["Stuart Russell",
           "Peter Norvig"]
}

Apenas como exemplo, veja como poderia ser a mesma informação disponibilizada em formato XML:

<livro>
    <ano>2009.0</ano>
    <autor>Stuart Russell</autor>
    <autor>Peter Norvig</autor>
    <edicao>3.0</edicao>
    <editora>Prentice Hall</editora>
    <id>98746</id>
    <isbn>
        <tipo>13</tipo>
        <valor>978-0136042594</valor>
    </isbn>
    <nome>Artificial Intelligence</nome>
    <preco>56.84</preco>
    <subtitulo>A Modern Approach</subtitulo>
</livro>

Falando sobre a JSON-P

Na prática a API, oferece ferramentas e mecanismos para trabalhar com arquivos e informações no formato JSON, como o descrito acima, possibilitando a leitura e escrita de uma forma padronizada. Embora a API seja conhecida como JSON-P, é importante saber que a mesma não tem relação com o conhecido “JSON with padding” ou  JSONP.

Esta API fornece duas forma diferentes de se trabalhar, através do “Modelo de Objeto” e “Modelo de Streaming”.

O Modelo de Objeto: Carrega toda a informação de um Objeto JSON na memória da aplicação, para que seja possível a sua manipulação, consome mais quantidade de memória que o modelo de streaming. Este modelo funciona de maneira semelhante a API DOM (Document Object Model) utilizada para manipulação de arquivos XMLs.

O Modelo de Streaming: Através de eventos específicos na leitura de um objeto JSON, é possível tratar os trechos de informação desejados, descartando as demais informações, e por este aspecto consome menor quantidade de memória que o modelo de objeto, pois seu processamento é realizado a medida em que a leitura ou escrita ocorre, sem a necessidade de colocar todo objeto JSON na memória. É o modelo mais indicado para o processamento de um grande número de informações.
Este modelo funciona de maneira semelhante a API SAX (Simple Api for XML) utilizada para manipulação de arquivos XMLs.

Por ser uma API da plataforma Enterprise Edition, pode ser utilizada em aplicações que executam em servidores de aplicação que atendem a especificação Java EE 7, sem qualquer importação de arquivos adicionais, porém caso deseje, é possível utilizá-la em aplicações que não necessitam dos servidores de aplicação, assim como em outras APIs do Java EE 7, para isto basta realizar a importação do arquivo “.jar” contendo a implementação da API, adicionando-o ao “classpath” da sua aplicação, também é possível fazer uso da mesma através do controle de dependências fornecido pela ferramenta “Maven”.

Vamos conhecer algumas das principais classes desta API, através dos exemplos abaixo:

Modelo de Objetos

Para construir um Objeto no formato JSON, através do modelo de Objetos, é preciso basicamente:

  • Criar um “builder” ou construtor  específico, seja ele para um Objeto ou Array, sendo representados pelas interfaces “JsonObjectBuilder” (javax.json.JsonObjectBuilder) ou “JsonArrayBuilder” (javax.json.JsonArrayBuilder).
  • para criação deste “builder”, pode ser utilizado os métodos de fabricação estáticos presentes na classe “Json” (javax.json.Json), sendo “Json.createObjectBuilder()” ou “Json.createArrayBuilder()” de acordo com o tipo JSON que se deseja criar.
  • a partir do “builder” obtido, adicionar toda informação desejada, por meio dos métodos de adição disponíveis (métodos “add”), lembrando que todo objeto JSON, as chaves são representadas por Strings, então as chamadas basicamente esperam receber o nome de uma chave, e o seu valor em seguida.
  • ao final, utilizar o método “build()“, para retornar o objeto construído, sendo uma implementação das interfaces “JsonObject” ou “JsonArray”

Vamos construir através da API o Objeto JSON citado previamente, para representar o nosso exemplo de “Livro”, conforme:

// Criando um Objeto JSON com Modelo de Objetos:

// Contruindo o ISBN do Livro
JsonObjectBuilder isbnBuilder = Json.createObjectBuilder();
isbnBuilder.add("tipo", 13);
isbnBuilder.add("valor", "978-0136042594");
JsonObject isbn = isbnBuilder.build();

// Construindo o Array de Autores do Livro
JsonArrayBuilder autoresBuilder = Json.createArrayBuilder();
autoresBuilder.add("Stuart Russell");
autoresBuilder.add("Peter Norvig");
JsonArray autores = autoresBuilder.build();

// Construindo o Livro.
// Utilizando Interfaces Fluentes através das chamadas do método "add" em sequência
JsonObject livro = Json.createObjectBuilder()
                       .add("id", 98746)
                       .add("nome", "Artificial Intelligence")
                       .add("subtitulo", "A Modern Approach")
                       .add("preco", 56.84)
                       .add("editora", "Prentice Hall")
                       .add("edicao", 3.0)
                       .add("isbn", isbn)
                       .add("autor", autores)
                       .add("ano", 2009.0)
                       .add("edicao",3.0)
                       .build();

System.out.println(livro);

A saída para esta chamada deve ser objeto JSON conforme mostrado no primeiro exemplo do artigo.

Observe que para as chamadas “.add(“isbn”, isbn)” e “.add(“autor”, autores);” o valor informado corresponde a um “JsonObject” e um “JsonArray“, e que a chamada aos métodos “add” para construção do “livro” utilizam um mecanismo denominado interface fluente, onde cada chamada ao método “add” retorna uma referência a mesmo objeto, neste caso o mesmo “builder”. Caso deseje alterar um determinado valor, basta chamar novamente o método “add” informando a “chave” e o novo valor desejado.

Para ler um Objeto JSON a partir do modelo de dados é preciso basicamente:

  • criar um leitor, representado pela interface “JsonReader” (javax.json.JsonReader)
  • para a criação deste leitor, é possível utilizar o método de fabricação estático “Json.createReader(…)“, podendo receber como parâmetro uma instância de qualquer classe que implemente “InputStream” (java.io.InputStream) ou “Reader” (java.io.Reader),
  • a partir do nosso “leitor”, chamar o método “readObject()” para obter um “JsonObject” (Objeto JSON), ou “readArray()” para obter um “JsonArray” (Array JSON),
  • caso possua um “JsonObject”, é possível ler as informações por métodos “get” passando o nome da “chave” desejada.
  • caso possua um “JsonArray”, é possível ler as informações utilizando o “índice” do valor desejado.

Exemplo da leitura para o JSON que representa o nosso livro:

// Cria nosso stream a ser utilizado
		 
URL urlDoWebService = new URL("http://localhost:8080/biblioteca-rest/loja/livro");
InputStream streamParaLeitura = urlDoWebService.openStream();

// Cria o nosso leitor
// O Stream informado se refere a uma URL, mas poderíamos estar lendo de um arquivo, por ex.
JsonReader leitorDeObjeto = Json.createReader(streamParaLeitura);
		
//Faz a leitura e retorna o nosso objeto
JsonObject livroJson = leitorDeObjeto.readObject();
		
//Construindo um Objeto da nossa aplicação com as informações de ISBN
Isbn isbn = new Isbn();
		
//Ajusta os valores, lendo a informação do JSON.
isbn.setTipo(livroJson.getJsonObject("isbn").getInt("tipo"));
isbn.setValor(livroJson.getJsonObject("isbn").getString("valor"));
		
System.out.println("Json Obtido: " + livroJson);
System.out.println("Nome do Livro: " + livroJson.getString("nome"));
System.out.println("Objeto da nossa aplicação: " + isbn);
	    
JsonArray autorJsonArray = livroJson.getJsonArray("autor");
System.out.println("Autores: " + livroJson.getJsonArray("autor"));
System.out.println("Primeiro nome de autor: " + autorJsonArray.getString(0));
System.out.println("Valor do Array que não existe, assumindo o default: " + autorJsonArray.getString(10, "Valor default definido, índice inválido"));

Observe que a linha “new URL(“http://localhost:8080/biblioteca-rest/loja/livro”)” e em seguida a chamada do “openStream()” faz a criação de um “InputStream” que neste exemplo corresponde a URL de um serviço RESTful, criado apenas para os nossos exemplos, poderia ser utilizado um caminho para um arquivo desejado, por exemplo.

A saída padrão para este código será:

Json Obtido: {"id":98746,"nome":"Artificial Intelligence","subtitulo":"A Modern Approach","preco":56.84,"isbn":{"tipo":13,"valor":"978-0136042594"},"editora":"Prentice Hall","edicao":3.0,"ano":2009.0,"autor":["Stuart Russell","Peter Norvig"]}
Nome do Livro: Artificial Intelligence
Objeto da nossa aplicação: Isbn [tipo=13, valor=978-0136042594]
Autores: ["Stuart Russell","Peter Norvig"]
Primeiro nome de autor: Stuart Russell
Valor do Array que não existe, assumindo o default: Valor default definido, índice inválido

Os métodos “get” são chamados de acordo com o tipo de valor que se deseja obter, com pré conhecimento para o tipo presente no JSON, para isto deve-se informar o nome da “chave”, semelhante as chamadas realizadas para um Map<Key/Value>, porém para cada tipo de valor, existe um método “get” específico. A construção para um objeto da aplicação deve ser feita criando-se uma instância da classe desejada, e a partir da leitura do JSON, asjustar no objeto os seus valores, como por exemplo feito no trecho a seguir :

//Construindo um Objeto da nossa aplicação com as informações de ISBN
Isbn isbn = new Isbn();		
//Ajusta os valores, lendo a informação do JSON.
isbn.setTipo(livroJson.getJsonObject("isbn").getInt("tipo"));
isbn.setValor(livroJson.getJsonObject("isbn").getString("valor"));

Os métodos “get” acionados para o objeto “JsonArray” tem por base a utilização de um determinado índice, caso o índice seja inválido uma exceção é lançada, como por exemplo “java.lang.IndexOutOfBoundsException“, ou se desejar pode se utilizar o método “get” informando um valor default a ser utilizado caso o índice seja inválido.

Modelo de Streaming

Para construir um Objeto no formato JSON, através do Modelo Streaming, é preciso basicamente:

  • Criar uma “fábrica” de “geradores de json”, representada pela interface “JsonGeneratorFactory” (javax.json.stream.JsonGeneratorFactory)
  • podemos obter nossa “fábrica”, através da chamada de “Json.createGeneratorFactory(Map<String, ?>)”, podemos informar algumas configurações nesta chamada, em forma de um “Map”
  • com a nossa “fábrica”, obtemos um gerador de JSON representado pela classe “JsonGenerator” (javax.json.stream.JsonGenerator)
  • como o nosso gerador, podemos escrever a informação JSON, começando pela “raiz”, através dos métodos que iniciam Arrays (.writeStartArray()) ou iniciam Objetos (.writeStartObject()).
  • seguindo o fluxo de escrita do JSON, podemos utilizar os métodos “write” para adicionar o valor desejado, e ao final de cada “bloco de escrita” os métodos “.writeEnd()“.
  • ao final finalizamos a escrita com o método “.close()

Exemplo da escrita do nosso exemplo de objeto “livro”, utilizando o Modelo de Stream:

//Criando JSON com Streaming API

//Configuração para escrita com indentação
// (espaços e quebras de linha adicionadas a informação)
Map<String, Object> config = new HashMap<String, Object>(1);
config.put(JsonGenerator.PRETTY_PRINTING, true);

//Criando nossa fábrica de Gerador de JSON
JsonGeneratorFactory fabricaDoGerador = Json.createGeneratorFactory(config);

//Criando nosso gerador de JSON, que terá a escrita na saída padrão 
JsonGenerator geradorDeJson = fabricaDoGerador.createGenerator(System.out);

//Escrevendo todo nosso Objeto JSON
geradorDeJson.writeStartObject()
             .write("id", 98746)
             .write("nome","Artificial Intelligence")
             .write("subtitulo", "A Modern Approach")
             .write("preco", 56.84)
             .writeStartObject("isbn").write("tipo", 13)
                                      .write("valor", "978-0136042594")
                                      .writeEnd() 
             .write("editora", "Prentice Hall")
             .write("edicao", 3.0)
             .write("ano", 2009.0)
             .writeStartArray("autor").write("Stuart Russell")
                                      .write("Peter Norvig")
                                      .writeEnd()
             .writeEnd()
             .close();

Observe que ao criarmos Objetos ou Arrays JSON, internamente a outro objetos, utilizamos os métodos “.writeStartObject(<nome da chave>)”, e “.writeStartArray(<nome da chave>)”, para que o mesmo seja escrito, tendo já atribúido a ele o nome de uma “chave” específica.

Neste exemplo, o método “close()” é acionado apenas ao final da escrita da informação, desta forma, neste momento toda a informação é direcionada para a saída configurada (configuração feita no trecho “fabricaDoGerador.createGenerator(System.out)”), se desejar realizar escritas parciais, e não apenas aguardar que o “close()” a realizse, é possível utilizar para isto o método “.flush()” do nosso gerador, contanto que o mesmo ainda não tenha sido fechado.

O resultado do código acima, será a exibição do nosso objeto JSON, como mostrado no primeiro exemplo, contendo toda indentação.

Para realizar a leitura de um Objeto no formato JSON, através do Modelo Streaming, é preciso basicamente:

  • obter uma “fábrica” para o objeto que irá ler a informação, conhecido como “parser”
  • podemos obter nossa “fábrica” que é representada pela interface “JsonParserFactory” (javax.json.stream.JsonParserFactory), através da chamada do método “Json.createParserFactory(Map<String, ?>)“, podendo informar algumas configurações nesta chamada em forma de um “Map”
  • com a nossa fábrica, podemos criar um parser, representado pela interface “JsonParser” (javax.json.stream.JsonParser) através dos diferentes métodos “.createParser(…)” disponíveis
  • durante a criação do parse, devemos informar qual será a a origem da leitura, através dos diferentes argumentos utilizados no método “.createParser(…)”
  • obtido o “parser” a informação já pode ser lida, de forma semelhante ao comportamento de um “Iterator”, retornando sempre algum “evento” específico na leitura do conteúdo, representado pelo Enum “Event” (javax.json.stream.JsonParser.Event)

Segue abaixo, um exemplo da leitura do nosso objeto JSON “Livro”, obtido através da chamada a URL de um Serviço RESTful:

// Lendo JSON com o Modelo de Streaming

// Cria a nossa URL do serviço, apenas criada para exemplos.
// obtém o "stream" para sua chamada.
URL urlDoWebService = new URL("http://localhost:8080/biblioteca-rest/loja/livro");
InputStream streamParaLeitura = urlDoWebService.openStream();

// Fábrica do nosso parser, sem informar qualquer configuração específica
JsonParserFactory fabricaDeParser = Json.createParserFactory(null);
//Obtém um parser, que irá ler as informações retornadas pelo nosso serviço
JsonParser parser = fabricaDeParser.createParser(streamParaLeitura);

// A medida em que lê o conteúdo, os eventos podem ser obtidos a partir do "parser"
while (parser.hasNext()) {
    //Um evento corresponde aos diferentes tipos da informação lida no JSON
    Event evento = parser.next();

    System.out.println("Evento obtido é do tipo: " + evento);

    // Podemos tomar uma decisão e escolher o que queremos ler ou não,
    // com base no tipo do Enum
    switch (evento) {
        case KEY_NAME: {
            System.out.println(" Encontramos uma chave, o seu nome é: " + parser.getString());
            break;
        }
        case START_OBJECT: {
            System.out.println(" Encontramos o inicio de um Objeto.");
            break;
        }
        case START_ARRAY: {
            System.out.println(" Encontramos o inicio de um Array.");
            break;
        }
    }
}

Observe que através da chamada “parser.hasNext()”, realizamos a leitura da informação enquanto houver o que ser lido, neste caso é possível saber que há o que ser lido através dos diferentes “eventos” encontrados no JSON que representa o nosso objeto “livro”.

Podemos ver Um exemplo de decisão a ser tomada, através do trecho:

...
case KEY_NAME: {
System.out.println(" Encontramos uma chave, o seu nome é: " + parser.getString());
break;
}
...

Onde todas as chaves do Arquivo tem seu “nome” exibido, um possível uso ser obter o nome de todas as chaves existentes, sem a necessidade de carregar toda informação obtida com o JSON na memória da aplicação, o quê ocorre de forma diferente ao modelo de objetos.

Podemos ver que a JSON-P tem como intuito atender a uma necessidade de padronizar a forma de trabalhar com informações no formato JSON na plataforma Java, e oferecer uma ferramenta já integrada as soluções Enterprise, que até a sua publicação não existia, inicialmente introduzida na versão 7 do Java EE.

Vimos que toda leitura e escrita entre os Objetos Java, é feita de forma manual, efetuando a leitura e escrita de cada “chave” ou trecho da informação desejada, através dos dois modelos distintos de trabalho.

Vale adiantar que a nova versão da JSON-P (1.1) está prevista para ser disponibilizada na versão 8 do Java EE, prevendo melhorias na API e um mecanismo de “binding” entre o texto JSON e os objetos Java, o quê hoje não existe, obrigando que toda leitura seja realizada de forma direta entre Objetos Java (POJOs, entidades da aplicação) X Informação JSON, e vice-versa, mais detalhes sobre a próxima versão nos links ao final do post.

Links e Referências externas

https://www.jcp.org/en/home/index – JCP Site.

https://jsonp.java.net/ – Site do Projeto Open Source, de implementação da referência da JSON-P.

https://json-processing-spec.java.net/nonav/releases/1.0/fcs/javadocs/index.html – Java DOC da versão 1.0 da API JSON-P.

http://docs.oracle.com/javaee/7/tutorial/ – Detalhes e tutorial do Java EE 7.

http://jcp.org/en/jsr/detail?id=342 – Especificação do Java EE 7 (atual versão enterprise).

http://jcp.org/en/jsr/detail?id=366 – Mais informações sobre o Java EE 8 (próxima versão enterprise).

http://pt.wikipedia.org/wiki/NoSQL – Banco de dados NoSQL

Webservices usando JAX-RS 2 e Spring

Neste post será mostrado como é possível criar facilmente um webservice RESTful utilizando Jersey 2 e Spring, a opção pelo Spring é para que seja feito o deploy usando Tomcat como container Web e desta maneira diminuir a complexidade da solução. Toda estrutura de dependências do projeto é gerenciada pelo Maven. Serão cobertos desde a configuração de dependências até testes automáticos da aplicação.

Continue lendo “Webservices usando JAX-RS 2 e Spring”