Dúvidas sobre JSON

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!

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;
}

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?

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.
}

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!

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.

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

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

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

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);
1 curtida

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!!

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?

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.

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!!

Ah, fui testar e deu erro:

[quote=leandropl]Ah, importei a biblioteca do axis2 e deu certo!!

Agora não consigo usar esse inputStream… :([/quote]

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:

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);
	}
}

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

Existem inúmeras bibliotecas para isso.

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á!!!

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!!!

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 = &quot;http://maps.google.com/maps/api/geocode/json?&quot; + 
									&quot;address=%s&sensor=true_or_false&quot;; // Tópico 1

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

			endereco = endereco.replace(&quot; &quot;, &quot;+&quot;); // 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(&quot;Não foi possível consultar o google maps para o endereço: &quot; + 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(&quot;Não foi possível converter o InputStream em uma String.&quot;, e); // Tópico 5
		} finally { // Tópico 5
			if (reader != null) {
				try {
					reader.close();
				} catch (IOException e) {
					throw new RuntimeException(&quot;Não foi possível converter o InputStream em uma String.&quot;, 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(&quot;Olá %s, bem vindo ao programa %s&quot;, &quot;Rafael&quot;, &quot;Mostrando como funciona o format.&quot;);
// &quot;Olá Rafael, bem vindo ao programa Mostrando como funciona o format.&quot;
String.format(&quot;Olá %s, bem vindo ao programa %s&quot;, &quot;Rafael&quot;);
// &quot;Olá Rafael, bem vindo ao programa &quot; -&gt; O último %s foi ignorado.

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

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

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

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.