Dúvidas sobre JSON

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

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 <T> T convert(InputStream json, Class<T> 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<Result> 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<Result> 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.

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?

Tudo bem!

Vamos por partes:

Sim. A assinatura do método é assim:

public <T> T convert(InputStream json, Class<T> 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?

[quote]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?[/quote]
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;
	}
}

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

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

Jesus…

[quote=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.[/quote]

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.

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