Dúvidas sobre JSON

28 respostas
L

Galera, existe um link json do google maps que me retorna um monte de informações de uma rua/avenida que eu queira pesquisar:

http://maps.google.com/maps/api/geocode/json?address=ENDEREÇO_DESEJADO&sensor=true_or_false

No caso eu preciso armazenar em String apenas a latitude e longitude desejada.

Pesquisando eu achei algumas informações sobre a biblioteca org.json, porém nada que me ensine como acessar um endereço json, armazenar ele em um objeto, buscar e extrair as informações que preciso.

Alguém poderia me dar uma luz nesse ponto?
Muito obrigado a todos que leram e possam ajudar!

28 Respostas

R

Primeiramente, você sabe o que é um JSON?

Existe uma biblioteca do google chamada GSON.

Primeiro você vai criar classes Java que vão ter os mesmos atributos (e tipos) que o json devolvido tem.
Depois, você vai receber uma String que vai ser aquele JSON todo e ai você usa o GSON para transformar o JSON nas suas classes.

Basicamente, um json assim:

{
   results: {
      size: 3,
      values: [
         "abc",
         "CDE",
         "efG"
      ]
   }
}

Seria o equivalente à:

public class Results {
   private int size;
   private List<String> values;
}
L

Rafael, sei muito pouco, estou pesquisando agora devido a um tcc que estou tentando fazer!
Mas entendi esse começo que você apresentou, só tenho uma dúvida por enquanto:
Eu preciso criar uma classe com TODOS os atributos que o Json tem ou apenas com os atributos que eu quero trabalhar?

O Json é muito extenso, eu só quero resgatar os dados deste trecho:

"geometry" : {
            "location" : {
               "lat" : -23.7050403,
               "lng" : -46.58616869999999
            },

Poderia criar uma classe do tipo?

public class Coordenadas {
 private List<String> valores;
}

Se isso for aceitável, como eu crio uma string para receber o json a partir de um link?

R

Bom, primeiro você vai ter que usar a API do java para acessar a URL. O nome da classe no java é? b[/b] :slight_smile:

Depois, o url vai ter retornar um InputStream, você lê ele, convertendo para uma String (existem bibliotecas que fazem isso).

Depois, você vai poder ler o JSON. Sim, você pode ter menos atributos.

No exemplo de JSON que você me deu, sua classe ficaria assim:

public class Coordenadas { // é o mesmo que o location, então tem que ter uma classe pai aqui (geometry)
   private Double lat; // Nesse caso são atributos e não uma lista.
   private Double lng; // Vale lembrar que são valores numéricos e não textos.
}
L

OK, agora você deu um nó na minha cabeça ou as 4 horas pesquisando nesse labirinto estão me deixando doido, rs!

Primeiro, onde eu consigo essa classe URL?? Pesquisei aqui e não acho nada!!

Esse método inputStream faz parte da classe URL ?

Essa parte de ler o JSON ainda ficou confusa, nem sei como perguntar ainda!

I

Neste caso json é apenas um formato conveniente escolhido pelo servidor, mas poderia ter sido html, xml, txt, etc… então definitivamente vc não precisa criar uma classe já que não existe nenhum mapeamento de objetos acontecendo.

Mas voltando ao ponto, pra acessar o conteúdo do json com sua biblioteca preferida, primeiro você precisa usar a classe URL pra baixar o conteúdo do arquivo pro seu computador como o Rafael Guerreiro falou.

L

Impossível, você poderia me dar uma dica de como conseguir essa classe url por favor?
Digo, eu importei um pacote import java.net.URL;

Mas quando faço a chamada:

URL myURL = new URL("http://example.com/");

Da erro e não explica o que é!!!
Eu realmente estou ficando sem opções aqui! haha

L

Ah, importei a biblioteca do axis2 e deu certo!!

Agora não consigo usar esse inputStream… :frowning:

R

Aqui e aqui tem exemplos de como usar a Classe URL do Java. Esse aqui é o método que você vai usar para pegar um InputStream (que vai ser o seu JSON).

Aqui tem um exemplo de como você pode transformar esse InputStream em String.

Aqui você encontra a documentação de como converter a sua String JSON em uma classe do Java que você vai criar. Procure tudo relacionado à Deserialization.

Esse ponto aqui (é da mesma página do link anterior) é bem interessante para você ver como que faz a conversão para uma classe que você criou:

// A class BagOfPrimitives é uma classe criada para mostrar como funciona quando você quer
// transformar de um JSON para uma class criada por você.
BagOfPrimitives obj2 = gson.fromJson(json, BagOfPrimitives.class);
L

Rafael, muito obrigado cara, hoje eu vou encerrar o expediente porque cérebro fritou, hahaha.
Mas amanhã, com mais calma, eu vou ler esses links que você mandou!!

L

Rafael, eu estou seguindo suas orientações e me deparei com um problema, segue o código:

public static void main(String[] args) throws Exception {
    	//cria a url
    	URL myURL = new URL("http://maps.google.com/maps/api/geocode/json?address="
    	        + "avenida+edilu+210+09861-400&sensor=true_or_false");
    	//o conteudo da url armazenado em um objeto
    	Object url2 = myURL.getContent();
    	
    	// ...?
    	InputStreamReader is = new InputStreamReader(myURL.openStream());
    	BufferedReader br = new BufferedReader(is);
    	StringBuilder sb = new StringBuilder();
  
    	String line;
		try {
			br = new BufferedReader(is);
			while ((line = br.readLine()) != null) {
				sb.append(line);
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (br != null) {
				try {
					br.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}	
		//testando saida
		System.out.println(sb.toString());   	
    }

Resultado:

{   "results" : [      {         "address_components" : [            {               "long_name" : "210",          ... ETC...

O resultado é todo o conteudo da URL em uma linha… Só assim que eu consigo converter a String em uma classe?

R

Vamos dar uma melhorada nesse código por que você não vai querer criar um único método gigante que faz muitas coisas.

Primeiro, crie uma classe e chame-a de GoogleMaps e crie um método mais ou menos assim: public InputStream getJsonForAddress(String address) {/… código …/}

Você não precisa usar o myURL.getContent().

Depois, crie OUTRA classe e chame-a de InputStreamConverter e crie um método assim: public String convert(InputStream is) {/… código …/}

Depois, crie OUTRA classe e chame-a de JsonConverter e crie um método assim: public <T> T convert (String json, Class<T> clazz){/… código …/}

E, por fim, crie outra classe que vai dar new nas 3 classes e invocar os métodos na ordem correta.

Depois que você fizer isso, eu te ajudo com a classe JsonConverter.

L

Certo vamos lá… eu fiz uma modificação na classe GoogleMaps, usei o inputStreamReader porque só com ele eu consegui usar o método openStream(). Fora isso eu fiz o que você me orientou:

GoogleMaps

package fatec.googleMaps;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;

public class GoogleMaps {
	
	private InputStreamReader is = null;
	
	public InputStreamReader getJsonForAdress(String endereço) 
										throws MalformedURLException{
		URL url = new URL(endereço);
		try {
			is = new InputStreamReader(url.openStream());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return is;
	}	
}

InputStreamConvert

package fatec.googleMaps;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class InputStreamConverter {
	
	public String convert(InputStreamReader is){
		
		BufferedReader br = new BufferedReader(is);
		StringBuilder sb = new StringBuilder();
		
		String line;
		try {
			br = new BufferedReader(is);
			while ((line = br.readLine()) != null) {
				sb.append(line);
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (br != null) {
				try {
					br.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}	
		
		return sb.toString();		
	}
}

JsonConverter

package fatec.googleMaps;

public class JsonConverter {
	
	public <T> T convert (String Json, Class<T> clazz){
		return null;
			
	}
	
}

Main

package fatec.googleMaps;

import java.io.InputStreamReader;
import java.net.MalformedURLException;

public class TesteCoordenadas {
	
	public static void main(String[] args) {
		
		String endereco = "Avenida Paulista";
		String numero = "14578";
		String cep = "01310-100";
		
		String url = "http://maps.google.com/maps/api/geocode/"
				+ "json?address="+endereco+"+"+numero+"+"+cep+"&sensor=true_or_false";
		
		//chamando e retornando o parametro da classe googleMaps
		GoogleMaps gm = new GoogleMaps();
		InputStreamReader isr = new InputStreamReader(null);
		try {
			isr = gm.getJsonForAdress(url);
		} catch (MalformedURLException e) {
			e.printStackTrace();
		}		
		//chamando e retornando o parametro da classe InputStreamConverter
		InputStreamConverter isc = new InputStreamConverter();
		String teste = isc.convert(isr);
			
	}			
}

Não sei se fiz certo, dava alguns erros e tive que dar uma gambiarra para dar certo!!

L

Ah, fui testar e deu erro:

I

leandropl:
Ah, importei a biblioteca do axis2 e deu certo!!

Agora não consigo usar esse inputStream… :(

Então, ao invés de usar a classe url que é muito baixo nível, eu prefiro usar o httpclient da Apache que ele abstrai essa parte de streams e retorna uma string direto. Assim vc evita ter que criar tanta classe pra algo tão banal como ler um mísero json. Mas é mais uma questão de gosto, tem gente que prefere quanto mais complicado melhor. :slight_smile:

R

Beleza, vamos por partes.

Primeiro, umas das ideias de encapsular todas as etapas é garantir que você não saia disparando exceptions que façam com que você fique espalhando catches pelo seu código inteiro. Checked exceptions só devem ser usadas quando você tem certeza de que precisa disparar uma. E esse não é o caso da MalformedURLException. Se a URL estiver mal-formada (que é o nome da exception) você pode retornar um null. Ou então colocar isso numa RuntimeException.

Sua classe GoogleMaps pode ficar assim:

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;

public class GoogleMaps {
	private static final String GOOGLE_MAPS_URL = "http://maps.google.com/maps/api/geocode/json?address=%s&sensor=true_or_false";

	// Evite acentuar variáveis. Isso pode te trazer MUITOS problemas
	public InputStream getJsonForAddress(String endereco) { // Address tem 2 'd' e 2 's'
		try {
			if (endereco == null)
				throw new IllegalArgumentException("O endereço não deve ser nulo.");

			endereco = endereco.replace(" ", "+");

			return new URL(String.format(GOOGLE_MAPS_URL, endereco)).openStream();
		} catch (IOException e) {
			throw new RuntimeException("Não foi possível consultar o google maps para o endereço: " + endereco, e);
		}
	}
}

O seu converter poderia ficar assim:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public class InputStreamConverter {

	public String convert(InputStream is) {
		BufferedReader reader = null;
		StringBuilder sb = new StringBuilder();

		String line;
		try {
			reader = new BufferedReader(new InputStreamReader(is));
			while ((line = reader.readLine()) != null) {
				sb.append(line);
			}
		} catch (IOException e) {
			throw new RuntimeException("Não foi possível converter o InputStream em uma String.", e);
		} finally {
			if (reader != null) {
				try {
					reader.close();
				} catch (IOException e) {
					throw new RuntimeException("Não foi possível converter o InputStream em uma String.", e);
				}
			}
		}

		return sb.toString();
	}
}

E a sua classe de teste poderia ficar assim:

public class TesteCoordenadas {

	public static void main(String[] args) {
		String endereco = "Avenida Paulista 14578 01310-100";
		// chamando e retornando o parametro da classe googleMaps
		InputStream is = new GoogleMaps().getJsonForAddress(endereco);
		// chamando e retornando o parametro da classe InputStreamConverter
		String teste = new InputStreamConverter().convert(is);
	}
}
R

@ImpossiveI, com certeza, mas achei que ele precisaria entender e conhecer os passos mais ao baixo nível.

Existem inúmeras bibliotecas para isso.

L

Opa Rafa, vou rodar esse código aqui e fazer os testes.
Ontem não consegui fazer porque estava sobrecarregado, mas hoje eu termino isso!!!
Muito obrigado desde já!!!

L

Opa, agora ta rodando sem erro, vou estudar o código para entender e poder apresentar ele em sala!! :slight_smile:

Agora só preciso ler ele e passar ele para um objeto correto?
Se for aqui ta a classe entidade que receberia os dados:

public class Coordenadas { 
   
	private Double lat;    
	private Double lng;   

	public Coordenadas(Double lat, Double lng) {
		super();
		this.lat = lat;
		this.lng = lng;
	}
	
	public Double getLat() {
		return lat;
	}
	
	public void setLat(Double lat) {
		this.lat = lat;
	}
	
	public Double getLng() {
		return lng;
	}
	
	public void setLng(Double lng) {
		this.lng = lng;
	}
      
}

Ps.: Muito obrigado por me apresentar o “endereco = endereco.replace(” ", “+”); " … Eu estava me matando para concatenar e resolver esse erro!!!

R

Eu vou comentar algumas etapas desses códigos para te ajudar a entender um pouco.
Vou numerar os tópicos e comentar abaixo.

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;

public class GoogleMaps {
	private static final String GOOGLE_MAPS_URL = "http://maps.google.com/maps/api/geocode/json?" + 
									"address=%s&sensor=true_or_false"; // Tópico 1

	public InputStream getJsonForAddress(String endereco) {
		try {
			if (endereco == null) // Tópico 2
				throw new IllegalArgumentException("O endereço não deve ser nulo."); // Tópico 2

			endereco = endereco.replace(" ", "+"); // Tópico 2

			return new URL(
						String.format(GOOGLE_MAPS_URL, endereco) // Tópico 1
					).openStream(); // Tópico 3
		} catch (IOException e) {
			throw new RuntimeException("Não foi possível consultar o google maps para o endereço: " + endereco, e); // Tópico 4
		}
	}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public class InputStreamConverter {

	public String convert(InputStream is) { // Tópico 3
		BufferedReader reader = null;
		StringBuilder sb = new StringBuilder(); // Tópico 4

		String line; // Tópico 4
		try {
			reader = new BufferedReader(new InputStreamReader(is)); // Tópico 3
			while ((line = reader.readLine()) != null) { // Tópico 4
				sb.append(line); // Tópico 4
			}
		} catch (IOException e) {
			throw new RuntimeException("Não foi possível converter o InputStream em uma String.", e); // Tópico 5
		} finally { // Tópico 5
			if (reader != null) {
				try {
					reader.close();
				} catch (IOException e) {
					throw new RuntimeException("Não foi possível converter o InputStream em uma String.", e);
				}
			}
		}

		return sb.toString(); // Tópico 4
	}
}

As linhas comentadas fazem parte de um mesmo tópico.
Tópicos:

1 - Sobre a formatação de Strings usando o String.format. O método format é um método estático que nos retorna uma nova String formatada. O primeiro argumento é uma String que deve ser usada como padrão. O format vai procurar pelas "variáveis" nessa String.

Olhando a String GOOGLE_MAPS_URL você vai ver que no meio dela você tem um %s. Isso significa que criamos uma variável para o format colocar nesse local, e o tipo dela é String (por isso o ‘s’). Se você quisesse passar 2 Strings, precisaria usar 2 %s nos locais desejados.

Exemplo:

String.format("Olá %s, bem vindo ao programa %s", "Rafael", "Mostrando como funciona o format.");
// "Olá Rafael, bem vindo ao programa Mostrando como funciona o format."
String.format("Olá %s, bem vindo ao programa %s", "Rafael");
// "Olá Rafael, bem vindo ao programa " -&gt; O último %s foi ignorado.

// Existem outros tipos para você concatenar usando o format:
String.format("Olá %s, bem vindo ao programa de número %d", "Rafael", 5);
// "Olá Rafael, bem vindo ao programa de número 5"

String.format("Olá %s, bem vindo ao programa de número %f", "Rafael", 5.0);
// "Olá Rafael, bem vindo ao programa de número 5.0"

String.format("Olá %s, bem vindo ao programa %b", "Rafael", true);
// "Olá Rafael, bem vindo ao programa true"

Aqui nesse link tem uma explicação mais aprofundada sobre o assunto, leia e TESTE os exemplos.

2 - Toda vez que você ver um erro de NullPointerException, a culpa será sua. O problema da NullPointerException é que ela não te diz quem está nulo, ou por quê uma determinada variável está nula.
Se nós tivéssemos omitido aquele “if” e o “throw new”, quando chamássemos o replace, ele dispararia um NullPointerException (caso o endereco seja nulo). Então, a gente SABE que não se pode buscar no google maps sem ter um endereço. Logo, se ele for nulo, nós temos um argumento ilegal no nosso método. Por isso soltamos um IllegalArgumentException. O melhor dela é que ela te fala qual é o problema, nós colocamos isso na mensagem!

Outra coisa, quando fazemos uma requisição alguns caracteres são ilegais, como o espaço e outros caracteres especiais. Por isso, fazemos o replace de espaço para o +. Pesquise melhor sobre como formatar a sua URL, pois esses caracteres especiais podem fazer com que ela não funcione e o seu InputStream volte nulo. Por isso você estava tendo uma NullPointerException, percebe como ela não te fala nada sobre o que está havendo?

3 - Assim como no tópico 2, caso o input stream seja nulo, nós temos um argumento ilegal. Primeira coisa, faça essa verificação e coloque uma mensagem adequada para que você saiba o que está acontecendo.

Você não precisa trabalhar com o InputStreamReader pois nós simplesmente criamos um quando formos ler o InputStream. Então trabalhe com InputStreams e, somente quando precisar ler, crie um Reader: new InputStreamReader(inputStream);

4 - Usamos StringBuilder aqui pois ele consegue concatenar Strings de uma forma incrivelmente rápida. Isso graças à forma como ele foi feito e graças ao método append.

Já a instrução do while pode parecer confusa e intimidante, mas ela é bem simples: while ((line = reader.readLine()) != null)
No Java, quando atribuímos um valor à uma variável, esse mesmo valor é retornado como resultado dessa expressão. Então:

int i = 2;
int j = (i = 25); // Aqui, i recebe 25 e depois j recebe 25 também.
int k = i = 2; // Aqui é igual o de cima só que sem os parêntesis.

Então temos uma variável String chamada line declarada anteriormente, depois, a cada verificação desse while, chamamos o método readLine() que retorna uma linha e colocamos dentro da variável line. Depois disso, verificamos se isso não for nulo, pois quando o método readLine() retornar nulo, quer dizer que não tem mais linhas para ele ler.

Depois, dentro do while, chamamos o método append do StringBuilder para concatenarmos essa linha nele.

No fim, mandamos o StringBuilder construir uma String com todas aquelas linhas que lemos. E, para isso, basta chamar o toString().

PS: Olhe o nome da classe "StringBuilder", ou seja, "Construtor de String".

5 - Um reader SEMPRE precisa ser fechado depois que terminamos de usar. Então, mesmo se algum erro acontecer, precisaremos fechá-lo. Por isso colocamos a instrução de close() dentro do bloco finally, que SEMPRE será executado. (mesmo se houver um return nos blocos anteriores.)

Bom, espero que essas observações ajudem, sinta-se livre para questionar e corrigir alguma besteira que eu possa ter dito.

L

Opa, entendi QUASE TUDO o que você falou, perfeito!!!
Só uma dúvida, no caso do StringBuilder, ele seria um vetor dinâmico que vai adicionando caracteres conforme vai recebendo?
Pensando na saída final da minha aplicação, um objeto contendo a latitude e longitude, eu teria que criar um for para percorrer esse vetor e buscando palavras chaves e armazenando os valores posteriores?

Exemplo, em determinado ponto do vetor vai ter o seguinte trecho:

Como eu quero a lat e a lng, eu deveria colocar no for algum método que além de reconhecer as duas sequências de caracteres, armazene os 10 caracteres posteriores a esta sequência, correto?

Foi o que eu consegui pensar com essa saída, mas não faço a minha ideia de como pesquisar isso no google!!! rsrs

R

Vamos lá! Eu estava vendo direitinho como que o Gson funciona e vi que podemos entregar um Reader para ele. Isso quer dizer que não é preciso converter de InputStream para String. Basta usar o new InputStreamReader(inputStream) na hora de chamar o método fromJson.

A Classe JsonConverter ficaria assim:

public class JsonConverter {
	public &lt;T&gt; T convert(InputStream json, Class&lt;T&gt; clazz) {
		// Verifica se está nulo para poder disparar um erro com mensagem mais decente.
		if (json == null)
			throw new IllegalArgumentException("O input stream não pode ser nulo.");

		// converte o json em InputStreamReader e chama o método fromJson do Gson.
		return new Gson().fromJson(new InputStreamReader(json), clazz);
	}
}

Para usar esse código você precisa criar classes que vão permitir que você faça esse parse:

public class Json {
		private List&lt;Result&gt; results;

		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + getOuterType().hashCode();
			result = prime * result + ((results == null) ? 0 : results.hashCode());
			return result;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			Json other = (Json) obj;
			if (!getOuterType().equals(other.getOuterType()))
				return false;
			if (results == null) {
				if (other.results != null)
					return false;
			} else if (!results.equals(other.results))
				return false;
			return true;
		}

		private GsonTest getOuterType() {
			return GsonTest.this;
		}

		@Override
		public String toString() {
			StringBuilder builder = new StringBuilder();
			builder.append("{results:'").append(results).append("'}");
			return builder.toString();
		}

		public List&lt;Result&gt; getResults() {
			return results;
		}
	}
public class Result {
		private Geometry geometry;

		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + getOuterType().hashCode();
			result = prime * result + ((geometry == null) ? 0 : geometry.hashCode());
			return result;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			Result other = (Result) obj;
			if (!getOuterType().equals(other.getOuterType()))
				return false;
			if (geometry == null) {
				if (other.geometry != null)
					return false;
			} else if (!geometry.equals(other.geometry))
				return false;
			return true;
		}

		private GsonTest getOuterType() {
			return GsonTest.this;
		}

		@Override
		public String toString() {
			StringBuilder builder = new StringBuilder();
			builder.append("{geometry:'").append(geometry).append("'}");
			return builder.toString();
		}

		public Geometry getGeometry() {
			return geometry;
		}
	}
public class Geometry {
		private Bound location;

		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + getOuterType().hashCode();
			result = prime * result + ((location == null) ? 0 : location.hashCode());
			return result;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			Geometry other = (Geometry) obj;
			if (!getOuterType().equals(other.getOuterType()))
				return false;
			if (location == null) {
				if (other.location != null)
					return false;
			} else if (!location.equals(other.location))
				return false;
			return true;
		}

		private GsonTest getOuterType() {
			return GsonTest.this;
		}

		@Override
		public String toString() {
			StringBuilder builder = new StringBuilder();
			builder.append("{location:'").append(location).append("'}");
			return builder.toString();
		}

		public Bound getLocation() {
			return location;
		}
	}
public class Bound {
		private Double lat;
		private Double lng;

		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + getOuterType().hashCode();
			result = prime * result + ((lat == null) ? 0 : lat.hashCode());
			result = prime * result + ((lng == null) ? 0 : lng.hashCode());
			return result;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			Bound other = (Bound) obj;
			if (!getOuterType().equals(other.getOuterType()))
				return false;
			if (lat == null) {
				if (other.lat != null)
					return false;
			} else if (!lat.equals(other.lat))
				return false;
			if (lng == null) {
				if (other.lng != null)
					return false;
			} else if (!lng.equals(other.lng))
				return false;
			return true;
		}

		private GsonTest getOuterType() {
			return GsonTest.this;
		}

		@Override
		public String toString() {
			StringBuilder builder = new StringBuilder();
			builder.append("{lat:'").append(lat).append("', lng:'").append(lng).append("'}");
			return builder.toString();
		}

		public Double getLat() {
			return lat;
		}

		public Double getLng() {
			return lng;
		}
	}

E, na hora de executar você faz assim:

public class TesteCoordenadas {
	public static void main(String[] args) {
		String endereco = "Avenida Paulista 14578 01310-100";

		InputStream is = new GoogleMaps().getJsonForAddress(endereco);

		Json teste = new JsonConverter().convert(is, Json.class);

		System.out.println(teste.toString());
	}
}

Não menos importante, sobre a sua dúvida, sim, o StringBuilder cria um vetor dinâmico de char. É mais ou menos o mesmo funcionamento de um ArrayList.

L

Opa Rafael, tudo bom?
Desculpa a demora, eu peguei o código para estudar, mas acabei estorando o prazo e para não perder a visita do orientador, adiantei outra parte do programa!!!
Mas vamos lá, eu acredito ter entendido boa parte do código.

O que eu não entendi é esse “public T convert…” e o “Class clazz…” ele retorna um tipo genérico de objeto?

Enfim, de resto eu entendi que esse método vai receber um inputStream e a classe que irá mapear o json, para depois chamar a classe gson e fazer a mágica mandando o inputStreamReader e o classe “Genérica” (?)

Minha ultima duvida seria a seguinte, a classe Bound irá armazenar o que eu preciso. Para acessar esse objeto eu teria que chamar como?

System.out.println(teste[1].getResults().getGeometry().getLocation().getBounds().getLat());
System.out.println(teste[1].getResults().getGeometry().getLocation().getBounds().getLgn());

Penso que como retorna um array e eu só quero o primeiro resultado, tenho que especificar o índice no começo, correto?

R

Tudo bem!

Vamos por partes:

Sim. A assinatura do método é assim:

public &lt;T&gt; T convert(InputStream json, Class&lt;T&gt; clazz);

T é a nossa variável que define que nós vamos colocar um Type lá. Ou seja, qualquer Tipo de Objeto. Primeiro usamos o <T> para definir o nome da nossa variável. Pode ser qualquer nome: “ABC”, “MeuTipo”, “TipoDaClass”. Mas, por definição do Java, T é o mais indicado. Existe um texto que explica melhor isso. Vá avançando que ele vai te mostrar tudo sobre Generics.

Então vamos imaginar o nosso T com um valor: BigDecimal. O java entenderia a nossa assinatura da seguinte forma:

public BigDecimal convert(InputStream json, Class&lt;BigDecimal&gt; clazz);

Então, dependendo da Class que informamos no parâmetro, ele vai pedir uma instância dessa classe como retorno.

É por isso que a List<String> não aceita um Integer no método add().

Hmmmm, quase. Só pelo detalhe de que não existe mágica no Java. Basicamente, esse método “fromJson” vai ler a sua classe informada e pegar os fields dela (atributos de class) que não forem transient ( private transient int esseFieldNaoVai; ) e vai procurar um atributo de MESMO nome na sua String. Então ele sabe quem é quem porquê ambos tem o mesmo nome. Simples, não?

Minha ultima duvida seria a seguinte, a classe Bound irá armazenar o que eu preciso. Para acessar esse objeto eu teria que chamar como?

System.out.println(teste[1].getResults().getGeometry().getLocation().getBounds().getLat());  
System.out.println(teste[1].getResults().getGeometry().getLocation().getBounds().getLgn());

Penso que como retorna um array e eu só quero o primeiro resultado, tenho que especificar o índice no começo, correto?


Errado.

1 - Ele não vai retornar um array. Ele vai retornar um objeto do tipo Json (sua class) quem CONTÉM uma lista (não um array). Então o começo seria assim:

test.getResults().get(1)...

2 - Só que fazer essa chamada encadeada não é nada bonito e nem prático. E pior, algum dia isso vai deixar a manutenção do seu código muito complexa. O que acontece se um método no meio desse pessoal retornar null? O que acontece se você decidir mudar a forma que essas classes se relacionam? Vai ser um caos. Ainda bem que existe a lei de demeter (LoD - Law of Demeter. E você deveria seguí-la)

Leia o link primeiro e depois continue lendo aqui.

Vou resumir um pouco para complementar o link: A lei diz que você não deve conhecer os objetos além do objeto que você está trabalhando. Logo, encadear essas chamadas é uma coisa que fere essa lei.

Que tal se você pudesse fazer algo assim:

List&lt;Bound&gt; bounds = teste.getAllLocations();

Não ficaria mais enxuto? Mais fácil de corrigir?

Como resolvemos isso? Começamos implementando os métodos de baixo para cima:

public class Result {
	// tudo que estava implementado

	public Bound getLocation() {
		if (geometry == null)
			return null; // Evitamos NullPointerException :)

		return geometry.getLocation();
	}
}
public class Json {
	// tudo que estava implementado

	public List&lt;Bound&gt; getAllLocations() {
		if (results == null)
			return null; // Evitamos NullPointerException :)

		List&lt;Bound&gt; locations = new LinkedList&lt;Bound&gt;();
		for(Result r : results)
			locations.add(r.getLocation());

		return locations;
	}
}
L

Rafael, eu estava tentando implementar o código hoje mas esta faltando uma classe gsonTest e o método getOuterType().
É de alguma biblioteca?

R

Isso é por que eu, por preguiça, implementei essas classes no meu eclipse dentro de uma classe que eu criei chamada GsonTest.

Apague os métodos private GsonTest getOuterType()

Depois, em cada classe aperte Alt + Shift + S (windows) ou Command + Option + S (MacOs), selecione a opção Generate hashCode() and equals() e depois clique nos OKs que aparecerem. Isso manda o eclipse gerar os métodos hashCode() e equals() usando os seus atributos.

É importante fazer isso em classes que carregam dados, como essas daí.

I

Jesus…

I

Rafael Guerreiro:
@ImpossiveI, com certeza, mas achei que ele precisaria entender e conhecer os passos mais ao baixo nível.

Existem inúmeras bibliotecas para isso.

Como fazer X foi o que ele perguntou. Nada contra conhecer como as coisas funcionam. Mas em qualquer outra situação nós responderíamos com a solução que representa o menor esforço (até porque quem esta começando não sabe o que é baixo/alto nível).

Mas como falei, nós programadores amamos complexidade no trabalho.

M

Me salvou, guerreiro. Muito obrigado! Vc não sabe o quanto procurei sobre isso até encontrar realmente mostre de forma simples.
Gratidão!

Criado 23 de março de 2015
Ultima resposta 14 de jun. de 2019
Respostas 28
Participantes 4