Este artigo ir apresentar a JSR 353, Java API for JSON Processing, que viabiliza e padroniza a criao e leitura de objetos JSON em Java. Veremos no artigo os dois modelos de leitura e gerao de contedo JSON, conhecidos como modelo de objetos e modelo de streaming. Ao final do artigo criaremos uma aplicao standalone utilizando a JSR como exemplo prtico. Com esta nova API o desenvolvedor poder, rapidamente, produzir e consumir dados em JSON. Sistemas de computadores geralmente no funcionam sozinhos, mas sim se integrando a outros sistemas, ora provendo, ora consumindo dados. Deste modo, os recursos envolvidos para viabilizar essa comunicao, como protocolo, APIs, formato de dados, etc. devem ser analisados quando projetamos qualquer aplicao. Diante de tudo isso, vlido lembrar que, dependendo da soluo, podemos aumentar ou reduzir a complexidade dessa interao. Na plataforma Java EE muito comum o uso do RMI, que a base do EJB, para interoperabilidade entre componentes de uma mesma aplicao. Mas esse tipo de protocolo binrio acaba sendo ineficiente como camada de integrao entre softwares distintos, pois cria um acoplamento muito forte entre eles (devido necessidade de exportar interfaces de servios e DTOs ver BOX 1 para a aplicao cliente), alm de permitir a integrao apenas entre softwares que usam esta tecnologia. BOX 1. Data Transfer Object DTO, sigla para Data Transfer Object, representa os objetos utilizados para transferir informaes entre camadas da aplicao, ou entre aplicaes distintas. J os protocolos baseados em texto, como o HTTP, permitem que criemos aplicaes em qualquer tecnologia/linguagem para servir ou consumir servios, alm de deixar a comunicao mais compreensvel para humanos, tornando mais fcil a depurao da troca de mensagens. Com este cenrio, os servios web, apoiados no HTTP, vm se consolidando como opo para integrao de aplicaes. At pouco tempo, o formato XML, aliado ao protocolo SOAP, era predominante no desenvolvimento de web services. O SOAP uma excelente soluo de integrao, sendo um protocolo muito bem definido e consolidado. Porm, muitas vezes o excesso de padronizao visto como um obstculo, pois faz com que a implementao seja mais trabalhosa. muito comum nos depararmos com problemas simples, e para solucion-los tambm comum estarmos dispostos a abrir mo de tanta padronizao em benefcio da agilidade. neste ponto que servios REST brilham, principalmente usando mensagens no formato JSON. Nos ltimos anos, a notao JSON vem se consolidando na preferncia dos desenvolvedores como o formato ideal para se trabalhar com web services. At a verso 6 da plataforma Java EE, no entanto, o Java no contava com uma API padro para o processamento de JSON. 2
Deste modo, cada projeto utilizava uma implementao prpria ou algumas conhecidas no mercado, como o caso do Jackson. A JSR 353 vem para preencher esta lacuna, embarcando nos containers Java EE uma implementao padronizada para criao e leitura de contedo JSON. Este artigo ir apresentar ao leitor a JSR 353 e como us-la para ler e gerar contedos JSON atravs de duas tcnicas conhecidas como modelo de objetos e modelo de streaming. Depois de apresentar a API, iremos construir um projeto de consulta de informaes climticas usando um servio web pblico que retorna JSON, como um caso de uso prtico da API. JavaScript Object Notation JSON JSON vem sendo largamente adotado principalmente no desenvolvimento de servios web. Grande parte deste crescimento se d pela flexibilidade do formato e tambm pela facilidade de leitura por humanos. Ao contrrio do XML, JSON muito menos verboso, pois no possui o sistema de tags, mais limpo e mais intuitivo. Uma informao no formato JSON pode ser apresentada de duas maneiras: como um objeto simples ou como uma lista (array). Um objeto uma sequncia de pares chave/valor, semelhante a um mapa, sempre envolvida por chaves ({}). Na estrutura do par chave/valor, a chave sempre uma String e o valor pode ser de seis tipos: uma String, um nmero (inteiro ou decimal), outro objeto, um array, true, false ou null. Cada chave separada do valor por dois pontos e os conjuntos chave/valor so separados entre si por vrgulas. Como o valor pode ser outro objeto, possvel formar uma estrutura hierrquica usando JSON. Diferentemente do objeto, o array envolvido por colchetes ([]) e seus elementos so separados por vrgula. Tais elementos no precisam ter a mesma estrutura ou ser do mesmo tipo. Em consequncia disso, podemos ter um array cujo primeiro elemento um objeto JSON e o segundo elemento pode ser outro array. A Listagem 1 mostra um objeto JSON vlido que contempla as possibilidades descritas. Listagem 1. Exemplo de objeto JSON vlido. { "editora" : "DevMedia", "ano" : 2021, "revista" : "Java Magazine", "edicao" : 200, "artigos" : [ { "titulo" : "JEE21 Criando um EJB com uma linha de cdigo", "caracteres" : 23000, "revisado" : true, "data" : "21/08/2021" }, { "titulo" : "JBrain Programming Java com comando cerebral", "caracteres" : 25000, "revisado" : false, "data" : null } ], "tiragem" : 120000 } JSR 353 3
De acordo com a especificao, a Java EE 7 prov uma API para analisar, transformar e fazer consultas a dados JSON usando um modelo de objetos ou um modelo de streaming. Na prtica isso significa que a API nos oferece dois modelos para trabalhar com JSON, a saber: O modelo de objeto: Neste modelo, tanto na leitura quanto na criao de um objeto JSON a API cria um objeto em memria, permitindo a leitura de seus dados e tambm a manipulao, como a alterao de valores ou a insero de novos valores na estrutura. Este modelo consome mais memria que o modelo de streaming, pois exige que o objeto seja carregado de uma vez para a memria; O modelo de streaming: Neste modelo a API disponibiliza um parser baseado em eventos para a leitura de objetos JSON. Durante o processamento do objeto, eventos do tipo iniciou um array, terminou um array, inicio um objeto, entre outros, permitem que desenvolvedores tratem apenas os blocos de dados que lhes interessem, descartando o restante. Devido a esta caracterstica, um modelo mais econmico para a memria, sendo indicado para grandes volumes de dados (ou para mquinas com pouca memria). A especificao define um conjunto de interfaces e classes nos pacotes javax.json e javax.json.stream que o desenvolvedor precisa utilizar para construir ou ler objetos no formato JSON. Veremos agora esses principais elementos com uma breve explicao de sua utilizao. Mais adiante veremos essas classes/interfaces em ao, lendo e gerando dados JSON. Json: Esta classe uma fbrica para criar objetos para processar JSON. Atravs dela obtemos objetos para criao e leitura de documentos JSON, tanto no modelo de objetos quanto no modelo de streaming; JsonArrayBuilder: Esta interface representa um builder de arrays JSON; JsonObjectBuilder: Esta interface representa um builder de objetos JSON; JsonStructure: Esta a superinterface dos tipos JsonArray e JsonObject; JsonArray: Esta interface representa um array imutvel de JSON. O item artigos da Listagem 1, por exemplo, quando lido pela API, transformado em um JsonArray; JsonObject: Esta interface representa um objeto JSON. Cada item da lista de artigos da Listagem 1, por exemplo, quando lido pela API, transformado em um JsonObject; JsonReader: Esta interface possibilita a leitura de um objeto ou array JSON a partir de uma determinada fonte; JsonGenerator: Esta interface possibilita a gerao de objetos JSON para um stream de sada; JsonParser: Esta interface prov um mecanismo de leitura de JSON a partir de um stream. Trabalhando com o Modelo de Objeto Para criar um objeto JSON usando o modelo de objeto, devemos instanciar um objeto do tipo javax.json.JsonObjectBuilder, ou do tipo javax.json.JsonArrayBuilder para criar um array. De acordo com a especificao, estes objetos devem ser criados por objetos builders (Builder Pattern) que so definidos pela API. Para obter os builders, usamos os mtodos estticos createObjectBuilder() ou createArrayBuilder() da classe Json. 4
Com o builder em mos podemos utilizar o mtodo add() sucessivamente para adicionar elementos ao objeto ou ao array e finalizar a construo usando o mtodo build(), que retorna um JsonObject ou um JsonArray (ambos so subclasses de JsonStructure). Os exemplos de cdigo desta seo so para demonstrar o funcionamento da API. Todo o cdigo est disponvel no site da revista e foi desenvolvido usando o Maven para controlar as dependncias do projeto e realizar o build. Mais adiante, como j informado, iremos expor a criao de um projeto usando a API e como adicionar as dependncias para conseguir executar uma aplicao standalone com tal recurso. A Listagem 2 mostra a criao de um objeto JSON usando o modelo de objeto. Listagem 2. Criando um objeto JSON usando o modelo de objeto. JsonObjectBuilder builder = Json.createObjectBuilder();
builder.add("titulo", "Curtindo a Vida Adoidado"); builder.add("ano", 1986); builder.add("diretor", "John Hughes"); builder.add("genero", "Comdia"); builder.add("oscar", false);
JsonObject json = builder.build();
System.out.println(json); Quando executado, um programa com o cdigo da Listagem 2 apresenta o seguinte resultado: {"titulo":"Curtindo a Vida Adoidado", "ano":1986, "diretor":"John Hughes", "genero":"Comedia", "oscar":false} Como explicado anteriormente, o builder JsonObjectBuilder constri objetos e o builder JsonArrayBuilder constri arrays. A Listagem 3 mostra a criao de um array JSON usando o modelo de objeto. Listagem 3. Criando um array JSON usando o modelo de objeto. JsonArrayBuilder builder = Json.createArrayBuilder();
System.out.println(json); O resultado da execuo desse cdigo : ["Java",true,8176,null,12.33] Como o leitor deve ter reparado, existem vrios mtodos add() sobrecarregados, tanto na classe JsonObjectBuilder quanto na classe JsonArrayBuilder. A invocao de cada um depende dos parmetros informados. A Listagem 4 exibe a lista de mtodos add() com seus respectivos parmetros e retornos. 5
JsonArrayBuilder add(BigDecimal value) JsonArrayBuilder add(BigInteger value) JsonArrayBuilder add(boolean value) JsonArrayBuilder add(double value) JsonArrayBuilder add(int value) JsonArrayBuilder add(JsonArrayBuilder builder) JsonArrayBuilder add(JsonObjectBuilder builder) JsonArrayBuilder add(JsonValue value) JsonArrayBuilder add(long value) JsonArrayBuilder add(String value) JsonArrayBuilder addNull() Na construo de um objeto ou de um array JSON, devemos chamar sucessivamente o mtodo add() do objeto builder para adicionar novos elementos. Toda chamada de add() retorna o prprio builder utilizado na invocao do mtodo. Esta caracterstica chamada de interface fluente, cujo uso reduz sensivelmente a quantidade de cdigo e facilita a leitura. A Listagem 5 mostra como fica o exemplo da Listagem 3 usando interface fluente. Listagem 5. Criando um objeto JSON usando o modelo de objeto e interface fluente. JsonObject json = Json.createObjectBuilder() .add("titulo", "Curtindo a Vida Adoidado") .add("ano", 1986) .add("diretor", "John Hughes") .add("genero", "Comedia") .add("oscar", false) .build();
System.out.println(json); Podemos tambm combinar builders de objetos e de arrays para criar objetos com valores do tipo array ou arrays com elementos do tipo objeto, pois a API permite o aninhamento de objetos e arrays. Este tipo de situao pode ser til em casos como o exposto na Listagem 1, no qual temos um objeto na raiz com informaes de uma revista, sendo que uma das chaves, artigos, remete a um array e cada item deste array ainda outro objeto. A Listagem 6 mostra como fazer o aninhamento. Listagem 6. Aninhando objetos e arrays em uma estrutura JSON. JsonObject json = Json.createObjectBuilder() .add("titulo", "Curtindo a Vida Adoidado") .add("ano", 1986) .add("diretor", Json.createObjectBuilder().add("nome", "John hughes").add("pais", "EUA")) .add("genero", Json.createArrayBuilder().add("comedia").add("aventura")) .add("oscar", false) 6
.build();
System.out.println(json); O resultado da execuo do cdigo da Listagem 6 : {"titulo":"Curtindo a Vida Adoidado", "ano":1986, "diretor":{"nome":"John hughes", "pais":"EUA"}, "genero":["comedia", "aventura"], "oscar":false} Usando o Modelo de Objeto para leitura de dados Anteriormente vimos como criar objetos ou arrays JSON usando a especificao atravs dos objetos builders. Alm de criar objetos, a API tambm prov formas de leitura de dados no formato JSON. Para isso, dispomos do objeto JsonReader. Para obter um JsonReader, usamos o mtodo esttico createReader() da classe Json. Este mtodo recebe como parmetro qualquer implementao de reader (da API java.io.Reader), como FileReader, BufferedReader, etc., e retorna um JsonReader. A Listagem 7 mostra como realizar a leitura de um JSON usando a API e considerando que o arquivo json.txt tenha o contedo da Listagem 1. Listagem 7. Leitura do contedo JSON de um arquivo usando o modelo de objeto. JsonReader reader = Json.createReader(new FileReader("/tmp/json.txt"));
JsonObject json = reader.readObject();
String revista = json.getString("revista"); int ano = json.getInt("ano"); JsonArray artigos = json.getJsonArray("artigos");
JsonObject primeiroArtigo = (JsonObject) artigos.get(0); String titulo = primeiroArtigo.getString("titulo");
System.out.println("Revista: " + revista); System.out.println("Ano: " + ano); System.out.println("Primeiro artigo: " + titulo); importante complementar que a interface JsonReader disponibiliza trs mtodos: 1) readObject() usado no exemplo assume que a raiz do contedo a ser lido um objeto e retorna um JsonObject; 2) readArray() assume que a raiz um array e retorna um JsonArray; e, 3) read(), que genrico e retorna um JsonStructure (superclasse de JsonObject e JsonArray) para situaes em que no sabemos o tipo do contedo e devemos trat-lo no cdigo. Com o objeto do tipo JsonObject em mos podemos fazer as leituras como se estivssemos com um Mapa de objetos (e estamos, pois JsonObject implementa Map), com a diferena que devemos invocar o mtodo getter de acordo com o tipo esperado do valor a ser retornado. Por exemplo, se usarmos getString(nome) porque sabemos que a chave nome ir retornar um valor do tipo String (contedo do nome), mas se usarmos getInt(idade) porque sabemos que a chave idade ir retornar um valor do tipo inteiro. Outro ponto que podemos verificar na Listagem 7 que na leitura do atributo artigos invocamos getJsonArray(), que retorna um JsonArray. Diferente de JsonObject, trabalhar com JsonArray equivalente a trabalhar com uma collection, visto que JsonArray implementa a interface Collection. Sendo assim, podemos usar o mtodo get(index) para buscar um elemento do JsonArray. 7
Usando o Modelo de Streaming para leitura de dados Como explicado no incio do artigo, o modelo de streaming indicado quando temos grandes volumes de dados de entrada, mas s nos interessa algumas partes desses dados. Ao contrrio do modelo de objeto que carrega toda a estrutura do JSON lido em memria para que naveguemos pela rvore, o modelo de streaming oferece uma interface de eventos para que possamos filtrar os contedos que nos interessam. Para usarmos o modelo de streaming invocamos o mtodo esttico createParser() da classe Json. Este mtodo recebe como parmetro qualquer implementao de InputStream e retorna um objeto do tipo JsonParser que se comporta como um iterator. Com o parser, invocamos o mtodo hasNext() como condio de um lao e o next() para ler o prximo evento. A Listagem 8 implementa esta leitura e imprime o objeto event em cada iterao. Listagem 8. Leitura do contedo JSON de um arquivo usando o modelo de streaming JsonParser parser = Json.createParser(new FileInputStream("/tmp/json.txt"));
while (parser.hasNext()) { JsonParser.Event event = parser.next(); System.out.println(event); } Considerando que o arquivo /tmp/json.txt tenha o contedo do exemplo da Listagem 1, a sada do cdigo apresentado deve ser idntica exibida na Listagem 9. direita de cada evento foi adicionada uma explicao do que est sendo iterado. Listagem 9. Resultado da leitura do JSON usando o modelo de streaming. START_OBJECT Encontrou o { no incio do arquivo, que caracteriza o incio de um objeto KEY_NAME Encontrou a chave editora VALUE_STRING Encontrou o valor Devmedia do tipo STRING KEY_NAME Encontrou a chave ano VALUE_NUMBER Encontrou o valor 2021 do tipo NUMBER KEY_NAME Encontrou a chave revista VALUE_STRING Encontrou o valor Java Magazine do tipo STRING KEY_NAME Encontrou a chave edicao VALUE_NUMBER Encontrou o valor 200 do tipo NUMBER KEY_NAME Encontrou a chave artigos START_ARRAY Encontrou o incio do array de artigos START_OBJECT Encontrou o incio do objeto artigo KEY_NAME Encontrou a chave titulo VALUE_STRING Encontrou o valor JEE21 Criando... do tipo STRING KEY_NAME Encontrou a chave caracteres VALUE_NUMBER Encontrou o valor 23000 do tipo NUMBER KEY_NAME Encontrou a chave revisado VALUE_TRUE Encontrou o valor do tipo TRUE KEY_NAME Encontrou a chave data VALUE_STRING Encontrou o valor 21/08/2021 do tipo STRING END_OBJECT Encontrou o fim do objeto artigo . . . END_ARRAY Encontrou o fim do array de artigos KEY_NAME Encontrou a chave tiragem VALUE_NUMBER Encontrou o valor 12000 do tipo NUMBER END_OBJECT Encontrou o fim do objeto } 8
A execuo do cdigo da listagem deve exibir apenas os tipos de evento. Por exemplo, KEY_NAME quando encontrou uma chave, ou VALUE_STRING quando encontrou um valor do tipo String. Nesse momento vale lembrar que um dado em JSON no nada mais do que uma sucesso de chaves e valores. Com esse tipo de informao o desenvolvedor consegue filtrar apenas os valores que lhe interessa. Para obter o valor, devemos usar os mtodos get do prprio objeto JsonParser. Estes mtodos so responsveis por recuperar o valor do objeto encontrado, seja ele uma chave (que sempre ser do tipo String) ou o valor de uma chave, que tem um getter para cada tipo. A Listagem 10 mostra um exemplo de leitura de um arquivo JSON usando o modelo de streaming que carrega e exibe os nomes de todas as chaves (primeiro if), valores do tipo String (tambm no primeiro if) e valores do tipo Number (segundo if). A condicional para capturar nomes de chaves e valores do tipo String est agrupada porque ambos usam o mesmo getter para recuperar o valor (getString()). No faria sentido em uma aplicao real este tipo de abordagem, mas aqui estamos usando-a apenas como exemplo da sintaxe. Listagem 10. Lendo valores usando o modelo de streaming JsonParser parser = Json.createParser(new FileInputStream("/tmp/json.txt"));
while (parser.hasNext()) { JsonParser.Event event = parser.next(); if (event == Event.KEY_NAME || event == Event.VALUE_STRING) { System.out.println(parser.getString()); } if (event == Event.VALUE_NUMBER) { System.out.println(parser.getLong()); } } Repare que invocamos o getString() do parser para chaves e valores String, e o getLong() para valores numricos. Em uma situao mais real, consideremos que o sistema deva ler apenas os nomes dos artigos, desprezando todo o resto. Para isto, devemos tratar apenas a leitura de valores para chaves de nome titulo. A Listagem 11 mostra como implementar este tipo de soluo. Listagem 11. Filtrando o contedo usando o modelo de streaming. JsonParser parser = Json.createParser(new FileInputStream("/tmp/json.txt"));
while (parser.hasNext()) { JsonParser.Event event = parser.next(); if (event == Event.KEY_NAME && parser.getString().equals("titulo")) { parser.next(); System.out.println(parser.getString()); } } Neste exemplo nos preocupamos apenas com os eventos que nos interessa para recuperar apenas uma parte da informao; no caso, o ttulo de cada artigo. Este tipo de soluo faz sentido em situaes nas quais possumos um grande volume de dados a ser lido, mas somente uma parte da informao necessria. Por exemplo, se nossa aplicao estiver rodando em uma PaaS (Plataforma como Servio), pode ser crucial usar o modelo de streaming para reduzir o custo do servio, visto que este, muitas vezes, cobra por dados trafegados. Produzindo JSON com o Modelo de Streaming 9
Assim como no modelo de objetos, tambm possvel gerar uma estrutura JSON usando o modelo de streaming. Para isso, devemos obter um objeto da classe JsonGenerator atravs do mtodo esttico createGenerator() da classe Json. Aps criar o objeto devemos usar os mtodos write() para a escrita no stream. No modelo de streaming, sempre iniciamos a gerao do JSON com o mtodo writeStartArray() (para um JSON com um array na raiz) ou writeStartObject() (para um JSON com um objeto na raiz). Para adicionar elementos estrutura usamos o mtodo write() com os parmetros chave e valor ou write() com apenas o parmetro valor para itens de um array. A Listagem 12 mostra como escrever um JSON usando o modelo de streaming. Listagem 12. Escrevendo um JSON usando o modelo de streaming. JsonGenerator gen = Json.createGenerator(new FileOutputStream("/tmp/json_out.txt"));
gen.writeStartObject() .write("nome", "Joao") .write("idade", 20) .write("profissao", "Programador") .writeStartArray("linguagens") .write("Java") .write("Scala") .write("Javascript") .write("C++") .write("Ruby") .writeEnd() .writeEnd() .close(); O cdigo foi propositalmente identado para facilitar a leitura e a compreenso do objeto criado. Se verificarmos o contedo do arquivo gerado pelo cdigo anterior, devemos encontrar o contedo da Listagem 13. Listagem 13. Sada do programa da Listagem 12. { "nome":"Joao", "idade":20, "profissao":"Programador", "linguagens":[ "Java", "Scala", "Javascript", "C++", "Ruby" ] } Consulta do Clima com a API de JSON-P Agora que o leitor j se familiarizou com a API para JSON da JSR 353, vamos criar uma aplicao que utiliza servios web baseados em JSON para consultar uma base de dados pblica de informaes climticas e apresentar ao usurio a temperatura, condies do cu e umidade de uma cidade escolhida atravs de uma interface grfica. importante ressaltar que todo container ter uma implementao da JSR embarcada, pois ela faz parte da Java EE 7. No entanto, em uma aplicao standalone necessrio importar a implementao de referncia para o classpath, como veremos adiante. 10
Alm da implementao de referncia da JSR 353, usaremos Swing para construir a interface grfica. Para iniciar o desenvolvimento da aplicao, o leitor deve criar um diretrio para hospedar o cdigo. Como o projeto usar o Maven para gesto de dependncias e build, devemos criar um arquivo nomeado pom.xml na raiz do diretrio criado. O contedo do pom.xml apresentado na Listagem 14. Listagem 14. Arquivo pom da aplicao de clima. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.devmedia.jsr353</groupId> <artifactId>clima</artifactId> <version>1.0.0</version>
</project> Observando atentamente esse arquivo, notamos que existem dois plugins informados na tag pluginManagement. O primeiro, maven-compiler-plugin, para configurar o compilador do Maven para usar o Java 7. 11
O segundo, exec-maven-plugin, nos permite executar a aplicao atravs de um comando Maven. Para isto, temos que configurar esse plugin informando a classe com.devmedia.jsr353.clima.main.ClimaFrame como ponto de incio, pois alm desta ser responsvel por renderizar uma janela, por questo de simplicidade tambm a usaremos para iniciar a aplicao, por isso ela ir conter o mtodo esttico main(). A vantagem de executar a aplicao usando o Maven no ter que informar manualmente as dependncias no classpath, como faramos no caso de executar a aplicao diretamente pelo comando java. Alm disso, o Maven tambm resolve as dependncias transitivas, ou seja, as dependncias das dependncias. Logo abaixo no XML esto as dependncias. A primeira, javax.json, a implementao de referncia da JSR 353. Depois temos a javax.ws.rs-api e a resteasy-client, que so, respectivamente, a API e a implementao de outra JSR para facilitar a criao de clientes de servios REST. Feitas as configuraes iniciais do projeto, devemos comear a desenvolver o cdigo que ir consumir o servio pblico de informaes climticas. Para atender este requisito, criaremos uma classe chamada ClimaService. Nesta classe teremos um mtodo chamado getClima() que receber como parmetro a cidade a ser consultada. O mtodo far uma chamada REST a um servio pblico e retornar um JSON com informaes climticas. Com a API de JSON-P iremos ler os dados retornados pelo servio e construir um objeto ClimaInfo com informaes de temperatura, umidade, nome da cidade encontrada e condies do cu. Para apresentar os dados ao usurio utilizaremos um JFrame chamado ClimaFrame com componentes como o JTextField, para ser o ponto de entrada dos dados, e JLabels, para exibir os resultados. Na Listagem 15 possvel analisar a implementao da classe ClimaService. Como um projeto Maven, crie o arquivo ClimaService.java dentro da pasta src/main/java a partir da raiz do projeto. Listagem 15. Cdigo da classe ClimaService. public class ClimaService {
return null; } } } Como pode ser verificado, no incio do mtodo getClima() estamos fazendo uma chamada para um servio web pblico. Para isso usamos classes como Client, WebTarget e Response, que fazem parte da nova API para clientes REST. No est no escopo deste artigo detalhar o uso desta API; basta o leitor entender que este cdigo nos abstrai de vrios detalhes de conexes e requisies com o protocolo HTTP e nos retorna um JSON na varivel clima. Para mais informaes sobre REST 2.0, leia o artigo publicado na Java Magazine 122. Apenas para depurao, o mtodo imprime na sada padro o valor bruto do JSON e inicia o processo de converso utilizando um StringReader. Como esse servio retorna um objeto na raiz, o primeiro objeto extrado um JsonObject. A partir da raiz extramos o objeto data, que tambm um JsonObject. Na sequncia verificamos se o objeto data contm o atributo error, utilizando o mtodo containsKey() da API. Caso identifique a presena deste atributo, o servio retorna null. Caso contrrio, inicia a leitura dos atributos do objeto, como cidade, temperatura, umidade e condies do cu e os converte para um objeto do tipo ClimaInfo, descrito na Listagem 16. Note que na converso estamos transformando a temperatura de Fahrenheit para Celsius e convertendo as condies de cu em um booleano apenas para informar se existem nuvens ou no (poderamos ser mais detalhistas neste ponto). Listagem 16. Implementao da classe ClimaInfo. public class ClimaInfo {
public String cidade; public String temperatura; public String umidade; public boolean nuvem;
} Para construir a tela que receber a entrada dos dados e apresentar os resultados, vamos utilizar um JFrame. A classe ClimaFrame est descrita na Listagem 17. Listagem 17. Implementao de ClimaFrame. public class ClimaFrame extends JFrame {
final JTextField textCity = new JTextField(); textCity.setLocation(30, 30); textCity.setSize(340, 50); textCity.setFont(new Font("Arial", Font.PLAIN, 24)); 13
getContentPane().add(textCity);
final JLabel labelTemp = new JLabel(); labelTemp.setLocation(30, 60); labelTemp.setSize(340, 250); labelTemp.setFont(new Font("Arial", Font.PLAIN, 100)); getContentPane().add(labelTemp);
/* Apenas parte do cdigo de construo do ClimaFrame exibido nesta listagem. O cdigo completo da aplicao, com os detalhes de construo do frame e de seus componentes, est disponvel no site da revista. */
final ClimaService climaService = new ClimaService();
textCity.addKeyListener(new KeyAdapter() { public void keyTyped(KeyEvent e) { if (e.getKeyChar() == KeyEvent.VK_ENTER) { String city = ((JTextField) e.getSource()).getText(); ClimaInfo info = climaService.getClima(city); if (info == null) { labelTemp.setText(""); labelCity.setText("Cidade no encontrada."); labelHumidity.setText(""); labelSun.setVisible(false); labelCloud.setVisible(false); } else { labelTemp.setText(info.temperatura + " \u00b0C"); labelCity.setText(info.cidade); labelHumidity.setText("Umidade: " + info.umidade + "%"); labelSun.setVisible(info.nuvem == false); labelCloud.setVisible(info.nuvem); } } } });
setVisible(true); }
public static void main(String[] args) { new ClimaFrame(); } } Como o leitor deve ter reparado, o cdigo da classe que implementa a janela da aplicao utiliza dois arquivos de imagem, sol.png e nuvem.png. Estas imagens sero exibidas dentro de JLabels dependendo do retorno do servio. A estrutura de diretrios do nosso projeto exemplo deve ficar semelhante Figura 1. 14
Figura 1. Estrutura do projeto Clima. Os arquivos com as imagens, assim como os cdigos, podem ser baixados no site da revista. Depois de criar as classes e inserir os arquivos com as imagens no projeto, podemos compil-lo e execut-lo usando os comandos do Maven. A Listagem 18 mostra como fazer isso. Listagem 18. Compilando e executando a aplicao. $ mvn clean install . . . $ mvn exec:java Aps a execuo do comando mvn clean install o Maven ir exibir no console uma srie de informaes referentes ao download das dependncias e tambm detalhes do processo de build. Estes logs no foram apresentados na listagem por questes de espao. Concludo o build com sucesso, o comando mvn exec:java utiliza um plugin do Maven para executar a aplicao, iniciando pela classe configurada no pom.xm,l conforme descrito anteriormente. A janela construda com a classe ClimaFrame ser exibida na tela contendo uma caixa de texto que permitir ao usurio informar o nome de uma cidade e pressionar Enter para realizar a busca dos dados. Feito isso, a consulta ao servio ser realizada, os dados sero lidos usando a API de JSON-P e os resultados sero apresentados, conforme a Figura 2. 15
Figura 2. Tela da aplicao sendo executada. Alm de apresentar os dados climticos na tela, a aplicao tambm imprimiu no console o valor bruto do JSON recebido pelo servio. Este resultado exposto na Listagem 19. Listagem 19. Retorno dos dados em JSON do servio de consulta do clima. {"apiVersion":"1.0", "data":{ "location":"Rio de Janeiro, BRA", "temperature":"82", "skytext":"Clear", "humidity":"55", "wind":"10", "date":"2014-03-15", "day":"Monday" } } Com o crescimento do uso de web services REST que produzem e consomem dados no formato JSON, a JSR 353 preencheu uma importante lacuna da Java EE referente ao processamento desse tipo de dado. Essa especificao foi elaborada pensando em duas formas distintas de uso, o modelo de objetos e o modelo de streaming. O modelo de objetos til quando temos um volume pequeno de dados para ser lido ou gerado, pois tanto na leitura quando na gerao os objetos so inteiramente carregados na memria. O modelo de streaming, por sua vez, atende grandes volumes de dados evitando grandes alocaes de memria, porm torna o desenvolvimento um pouco menos simples do que o modelo de objetos. Agora que o processamento de dados do tipo JSON est previsto em especificao, passa a ser um item a menos que o desenvolvedor deve se preocupar caso opte por alterar o container em que a sua aplicao ser executada. Alm disso, vale ressaltar que o ganho no est somente na portabilidade, pois agora o desenvolvedor tambm no precisa mais implementar um parser ou importar um parser de terceiros, reduzindo assim a complexidade da prpria aplicao. 16
Links Pgina do IETF que define o padro JSON http://tools.ietf.org/html/rfc4627 Pgina da documentao da API http://docs.oracle.com/javaee/7/api/javax/json/package-summary.html Pgina da JSR 353: Java API for JSON Processing. http://jcp.org/en/jsr/detail?id=353 API pblica para obter informaes sobre a previso do tempo. http://www.previsaodotempo.org