Ajax e Json

Ajax e JSON são uma combinação perfeita para transitar dados entre o cliente e o servidor sem ter que atualizar a página ou sofrer para manipular os dados retornados por requisições XMLHttpRequest, em XML, HTML ou texto puro.

Ontem mostrei um tutorial com várias informações sobre como utilizar Ajax para poder enviar e receber dados do lado do cliente para o lado do servidor sem ter que atualizar a página.

Hoje vamos continuar na mesma linha de raciocínio, porém, também aprenderemos a criar e manipular dados de notação de objeto JavaScript (JSON).

JSON

Talvez você já saiba, mas vale ressaltar: JSON (JavaScript Object Notation) é uma formatação de texto leve para troca de dados entre cliente e servidor.

Além de leve, é fácil de ser lido e/ou escrito, tanto por humanos quanto por máquinas.

Outro ponto importante, é que JSON não é uma parte de Javascript (apesar de seu nome). Você pode utilizá-lo em várias outras linguagens de programação, como PHP, C++, e demais.

O media-type oficial do JSON é application/json e sua extensão é .json, isso quer dizer que você pode criar arquivos de texto contendo dados JSON [Veja mais aqui].

O formato mais simples de um arquivo JSON é:

{
	"nome" : "valor"
}

Onde o nome da propriedade fica no lado esquerdo, e o valor dessa propriedade no lado direito, separados por dois pontos (:).

Caso queira adicionar várias propriedades, você deve separá-las por vírgula:

{
	"nome" : "Luiz Otávio",
	"sobrenome" : "Miranda",
	"hobbies" : "Desenvolver"
}

Qualquer propriedade pode contém mais de um valor, no entanto, para atingir este objetivo é necessário utilizar colchetes e colocar todos os seus valores separados por vírgula.

Repare na propriedade hobbies abaixo:

{
	"nome" : "Luiz Otávio",
	"sobrenome" : "Miranda",
	"hobbies" : [ "Desenvolver", "Ler", "Tocar" ]
}

E você também pode complicar as coisas e aninhar milhares de informações, umas dentro de outras.

Veja hoobies agora:

{
	"nome" : "Luiz Otávio",
	"sobrenome" : "Miranda",
	"hobbies" : 
	[
		{ 
			"Desenvolver" : 
				[
					"Em Javascript", 
					"PHP"
				] 
		},
		{ 
			"Ler" : 
			[
				"Tech", 
				"Notícias"
			] 
		},
		{ 
			"Beber" : "Cerveja" 
		}
	]
}

Para aninhar propriedades, você utiliza um novo par de chaves e as cria dentro dessas chaves:

{
	"Nome" : "Luiz Otávio",
	"Dados" : 
	[
		{ "Desenvolver" : ["Em Javascript", "PHP"]},
		{ "Ler" : ["Tech", "Notícias"] },
		{ "Beber" : "Cerveja", "Comer" : "Churrasco" }
	],
	"Nome2" : "Letícia",
	"Dados2" : 
	[
		{ "Desenvolver" : "PHP" }
	]
}

JSON não é nada complicado, de fato, se você olhar para os trechos anteriores por alguns minutos, vai compreender como é fácil aninhar as informações que você precisa.

Esses dados JSON pode estar em qualquer tipo de arquivo, sejam eles: HTML, PHP, TXT, JSON, ou em uma variável Javascript.

var dadosJSON = {
	"Nome" : "Luiz Otávio",
	"Dados" : 
	[
		{ "Desenvolver" : ["Em Javascript", "PHP"]},
		{ "Ler" : ["Tech", "Notícias"] },
		{ "Beber" : "Cerveja", "Comer" : "Churrasco" }
	],
	"Nome2" : "Letícia",
	"Dados2" : 
	[
		{ "Desenvolver" : "PHP" }
	]
};

Que, na verdade, se tornam objetos literais e podem ser acessados como tal:

var dadosJSON = {
	"Nome" : "Luiz Otávio",
	"Dados" : 
	[
		{ "Desenvolver" : ["Em Javascript", "PHP"]},
		{ "Ler" : ["Tech", "Notícias"] },
		{ "Beber" : "Cerveja", "Comer" : "Churrasco" }
	]
};
							
var div = document.getElementById("texto");
div.innerHTML = 'Nome: ' + dadosJSON.Nome;
div.innerHTML += '<br>Desenvolver: ' + dadosJSON.Dados[0].Desenvolver;
div.innerHTML += '<br>Ler: ' + dadosJSON.Dados[1].Ler;
div.innerHTML += '<br>Beber: ' + dadosJSON.Dados[2].Beber;
div.innerHTML += '<br>Comer: ' + dadosJSON.Dados[2].Comer;

Perceba que à medida que você vai aninhando dados, também aumenta a complexidade para retornar esses dados. Se você ainda não percebeu, está aninhando propriedades de objetos dentro de arrays, arrays dentro de propriedades de objetos e assim por diante. Portanto, é importante que você entenda que JSON, em Javascript, é a mesma coisa que objetos literais misturados com arrays.

Quando você utiliza JSON diretamente em uma variável literal, não precisa fazer mais nada além de acessar os dados. Porém, quando eles vêm de fora (de um arquivo externo), você precisa converter esses dados em objetos utilizando duas maneiras disponíveis: eval(), que não é uma maneira segura, e JSON.parse, que é uma maneira segura, mas não é suportada por navegadores anteriores ao Internet Explorer 8.

Ajax e JSON

Normalmente, quando trabalho com JSON, estou fazendo alguma requisição utilizando Ajax (do mesmo modo que vimos na aula anterior) e tenho que pegar dados de uma base de dados, de um arquivo de configurações, ou quaisquer tipos de dados que devem ser retornados para o cliente, para fazer alguma ação.

Para nosso exemplo, e também para não entrar em contato com outras linguagens de programação, vou utilizar um arquivo que já contém as informações que desejo. Em seguida, vou carregar o conteúdo desse arquivo dentro do texto de uma DIV, apenas para que você entenda como funciona as requisições Ajax em conjunto com os dados do tipo JSON.

Organizando o meio de campo

Antes de qualquer coisa, vamos criar nosso arquivo JSON.

Para isso, simplesmente abra seu editor de textos favorito (recomendo o Notepad++) e coloque esses dados dentro dele.

Arquivo dados.json

{
	"nome" : "Luiz Otávio",
	"sobrenome" : "Miranda",
	"hobbies" : 
	[
		{ 
			"Desenvolver" : 
				[
					"Em Javascript", 
					"PHP"
				] 
		},
		{ 
			"Ler" : 
			[
				"Tech", 
				"Notícias"
			] 
		},
		{ 
			"Beber" : "Cerveja" 
		}
	]
}

Salve com qualquer extensão que preferir, mas, se quiser seguir o padrão, salve como "dados.json".

Agora, em nosso arquivo Javascript, vamos criar várias funções, cada uma delas para suprir uma necessidade.

Arquivo HTML (index.html)

É claro que também precisamos de um arquivo HTML com todas as tags necessárias. Creio que não preciso descrever o motivo, depois de tantas aulas que já tivemos.

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		
		<title>Demo</title>
		<script src="meu_arquivo_javascript.js"></script>
	</head>

	<body>
		<a href="" id="a">Clique</a>
		<div id="texto"></div>
	</body>
</html>

Captura / Cancela eventos

A primeira função que vamos precisar em nosso arquivo JavaScript (meu_arquivo_javascript.js), é a captura de eventos (já falamos sobre isso em "Eventos em Javascript"):

// Função para capturar eventos
function capturaEventos(obj, evt, fn) {
	// Verifica se o objeto suporta addEventListener
	if(obj.addEventListener){
		obj.addEventListener(evt, fn, true);
	} 
	// Adiociona attachEvent da Microsoft
	else {
		var evento = 'on' + evt;
		obj.attachEvent(evento, fn);
	}
}

A segunda é a função para cancelar os eventos:

// Cancela evento
function cancelaEvento(evt){
	// Verifica se o evento suporta stopPropagation
	if(evt.stopPropagation) {
		// Aplica-se para Firefox, Chrome e demais
		evt.stopPropagation();
		evt.preventDefault();
	} else {
		// Aplica-se para IEs antigos
		evt.cancelBubble = true;
		evt.returnValue = false;
	}
}

Criando um objeto XMLHttpRequest

Agora precisamos criar uma instância do objeto XMLHttpRequest (falamos sobre isso na última aula sobre Ajax):

// Função para capturar a requisição XMLHttpRequest
function verificaXmlHttp() {
	// Uma variável sem valor
	var xmlhttp;
	// Verifica se suporta XMLHttpRequest
	if (window.XMLHttpRequest) {
		// Adiciona o valor à variável
		xmlhttp = new XMLHttpRequest();
	} else {
		// Adiciona ActiveXObject da Microsoft 
		xmlhttp = new ActiveXObject('Microsoft.XMLHTTP');
	}
	// Retorna o valor
	return xmlhttp;
}

Capturando os eventos

Agora, vamos utilizar a função que criamos para capturar os eventos. O palco do show será dentro dela, onde todas as ações serão executadas:

// Captura evento load da página
capturaEventos(window, 'load', function(evt){
	// A ação vai ocorrer aqui
});

Dentro da função anterior, vamos localizar o elemento <a> (link) na nossa página HTML, capturar o evento de clique nesse link, e, dentro do evento de clique, vamos criar nossa requisição Ajax.

// Captura evento load da página
capturaEventos(window, 'load', function(evt){
	// Localiza o link com id "a"
	var a = document.getElementById('a');
	
	// Captura o evento de clique neste link
	capturaEventos(a, 'click', function(evt){
		var xmlhttp = verificaXmlHttp();

		// Verifica os estados da requisição
		xmlhttp.onreadystatechange = function(){
			// Verifica se a página foi carregada corretamente
			if(xmlhttp.readyState === 4 && xmlhttp.status === 200) {
				var dadosJSON;
				try {
					dadosJSON = JSON.parse(xmlhttp.responseText);
				} catch(e) {
					eval("dadosJSON = (" + xmlhttp.responseText + ");");
				}
			}
		}
		
		// Abre a requisição com o método e url
		xmlhttp.open('GET', 'dados.json', true);
		// Modifica o MimeType da requisição
		xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
		// Envia os valores
		xmlhttp.send(null);
		
		// Checagem para os IEs antigos
		var evento = evt ? evt : window.event;
		// Cancela o evento
		cancelaEvento(evento);
	});
});

A requisição Ajax que mostrei aqui, é a mesma que expliquei na aula anterior, porém, ao invés de fazer tudo diretamente, separei algumas partes – que são reutilizáveis – em funções separadas (também já vimos sobre funções em Javascript).

Veja a parte em que chamamos a função verificaXmlHttp() para criar a instância do objeto XMLHttpRequest:

var xmlhttp = verificaXmlHttp();

Depois que tenho a instância do objeto, posso continuar trabalhando meu código, como fizemos na aula anterior.

A parte final da função:

// Checagem para os IEs antigos
var evento = evt ? evt : window.event;
// Cancela o evento
cancelaEvento(evento);

Serve para que a página não seja atualizada quando o usuário clicar no link. Se isso acontecer, perderemos a requisição Ajax, que está ali justamente para que a página não atualize.

A parte que vamos focar neste artigo é a seguinte:

var dadosJSON;
try {
	dadosJSON = JSON.parse(xmlhttp.responseText);
} catch(e) {
	eval("dadosJSON = (" + xmlhttp.responseText + ");");
}

Que acontece depois que a requisição Ajax foi processada e os dados foram retornados para a página em que o cliente está.

xmlhttp.responseText

Retorna o texto do arquivo dados.json, que é o fizemos no início dessa sessão anterior. Porém, se você tentar ver esses dados em um depurador Javascript, terá exatamente isso em uma string:

Dados JSON em uma string

Para nós, não é interessante que os dados sejam retornados em uma string, mas sim em um objeto Javascript.

Em navegadores mais novos, basta utilizar JSON.parse, atribuir o valor a uma variável e estamos prontos, já temos nosso objeto e podemos acessar. Porém, em navegadores mais velhos, que não suportam JSON.parse, uma das maneiras de forçar uma string a se tornar um objeto é tentar utilizar eval().

Existem várias coisas a se levar em consideração ao utilizar eval(), pois, essa função avalia o texto de uma string e tenta fazer com que ela se transforme em código válido. Com isso, você já pode imaginar que se o texto da string puder ser manipulado por pessoas maliciosas, elas podem fazer o que quiserem dentro do seu código.

O conselho aqui é que você deve estudar muito bem como a sua aplicação vai funcionar, antes de utilizar essa função.

Voltando ao nosso código, quando utilizamos:

var dadosJSON;
try {
	dadosJSON = JSON.parse(xmlhttp.responseText);
} catch(e) {
	eval("dadosJSON = (" + xmlhttp.responseText + ");");
}

Verificamos se a primeira parte do código funciona no navegador em que está sendo executada:

dadosJSON = JSON.parse(xmlhttp.responseText);

Se o código acima não funcionar, outro trecho é passado:

eval("dadosJSON = (" + xmlhttp.responseText + ");");

A primeira parte é para navegadores mais novos, a segunda vai funcionar com versões antigas do IE6 e 7, e as outras versões que não suportam JSON.parse.

Depois que você tem o objeto, pode acessar os dados como preferir.

Normalmente, desenvolvedores não sabem quais valores virão nos seus dados JSON, e você não precisa limitar sua aplicação se não souber.

Apesar de todos os níveis do nosso arquivo JSON, podemos ainda utilizar um laço for … in para acessar os valores de maneira automática:

// Localiza nossa div dentro do HTML
var div = document.getElementById('texto');

// Utiliza um laço for ... in
for( var propriedade in dadosJSON ){
	// Adiciona a propriedade no texto da div
	div.innerHTML += propriedade + ' = ';
	
	// Verifica se a propriedade é um objeto
	if (typeof dadosJSON[propriedade] !== 'object') {
		// Adiciona o valor da propriedade no texto da div
		div.innerHTML += dadosJSON[propriedade] + '<br>';
	} else { 
		// Se for objeto, acessa o valor da maneira alterativa e 
		// adiciona na div
		div.innerHTML += '<br>';
		div.innerHTML += 'Desenvolver: ' + dadosJSON[propriedade][0].Desenvolver;
		div.innerHTML += '<br>';
		div.innerHTML += 'Ler: ' + dadosJSON[propriedade][1].Ler;
		div.innerHTML += '<br>';
		div.innerHTML += 'Beber: ' + dadosJSON[propriedade][2].Beber;
	}
}			

Já falamos sobre este tipo de laço em nosso artigo "Laços em Javascript", mesmo assim, perca um tempinho lendo os comentários que deixo em todos os trechos de código dessa página, eles podem ajudar no seu entendimento.

Javascript completo

Caso você tenha se perdido no código, segue abaixo o Javascript completo utilizado nessa aula:

// Função para capturar eventos
function capturaEventos(obj, evt, fn) {
	// Verifica se o objeto suporta addEventListener
	if(obj.addEventListener){
		obj.addEventListener(evt, fn, true);
	} 
	// Adiociona attachEvent da Microsoft
	else {
		var evento = 'on' + evt;
		obj.attachEvent(evento, fn);
	}
}

// Cancela evento
function cancelaEvento(evt){
	// Verifica se o evento suporta stopPropagation
	if(evt.stopPropagation) {
		// Aplica-se para Firefox, Chrome e demais
		evt.stopPropagation();
		evt.preventDefault();
	} else {
		// Aplica-se para IEs antigos
		evt.cancelBubble = true;
		evt.returnValue = false;
	}
}

// Função para capturar a requisição XMLHttpRequest
function verificaXmlHttp() {
	// Uma variável sem valor
	var xmlhttp;
	// Verifica se suporta XMLHttpRequest
	if (window.XMLHttpRequest) {
		// Adiciona o valor à variável
		xmlhttp = new XMLHttpRequest();
	} else {
		// Adiciona ActiveXObject da Microsoft 
		xmlhttp = new ActiveXObject('Microsoft.XMLHTTP');
	}
	// Retorna o valor
	return xmlhttp;
}

// Captura evento load da página
capturaEventos(window, 'load', function(evt){
	// Localiza o link com id "a"
	var a = document.getElementById('a');
	
	// Captura o evento de clique neste link
	capturaEventos(a, 'click', function(evt){
		var xmlhttp = verificaXmlHttp();

		// Verifica os estados da requisição
		xmlhttp.onreadystatechange = function(){
			// Verifica se a página foi carregada corretamente
			if(xmlhttp.readyState === 4 && xmlhttp.status === 200) {
				var dadosJSON;
				try {
					dadosJSON = JSON.parse(xmlhttp.responseText);
				} catch(e) {
					eval("dadosJSON = (" + xmlhttp.responseText + ");");
				}

				// Localiza nossa div dentro do HTML
				var div = document.getElementById('texto');

				// Utiliza um laço for ... in
				for( var propriedade in dadosJSON ){
					// Adiciona a propriedade no texto da div
					div.innerHTML += propriedade + ' = ';
					
					// Verifica se a propriedade é um objeto
					if (typeof dadosJSON[propriedade] !== 'object') {
						// Adiciona o valor da propriedade no texto da div
						div.innerHTML += dadosJSON[propriedade] + '<br>';
					} else { 
						// Se for objeto, acessa o valor da maneira alterativa e 
						// adiciona na div
						div.innerHTML += '<br>';
						div.innerHTML += 'Desenvolver: ' + dadosJSON[propriedade][0].Desenvolver;
						div.innerHTML += '<br>';
						div.innerHTML += 'Ler: ' + dadosJSON[propriedade][1].Ler;
						div.innerHTML += '<br>';
						div.innerHTML += 'Beber: ' + dadosJSON[propriedade][2].Beber;
					}
				}				
			}
		}
		
		// Abre a requisição com o método e url
		xmlhttp.open('GET', 'dados.json', true);
		// Modifica o MimeType da requisição
		xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
		// Envia os valores
		xmlhttp.send(null);
		
		// Checagem para os IEs antigos
		var evento = evt ? evt : window.event;
		// Cancela o evento
		cancelaEvento(evento);
	});
});

Isso mesmo, 102 linhas de puro Javascript só para capturar um clique e executar uma requisição Ajax!

Download

Você também pode baixar os arquivos utilizados nessa aula no link abaixo:

Concluindo

De início, pode parecer extremamente complicado trabalhar com Ajax e JSON, principalmente quando os dados são aninhados em várias camadas. No entanto, você só vai aprender se "quebrar" um pouco a cabeça para tentar resolver possíveis problemas que poderão ocorrer na sua aplicação.

Qualquer dúvida, basta perguntar nos comentários.

Outras aulas

Caso tenha perdido a aula anterior, segue o link:

Caso queira visualizar todas as aulas dessa sessão em ordem cronológica invertida: