Digamos que temos um objeto complexo e gostaríamos de convertê-lo em uma string, enviá-lo por uma rede ou apenas exibi-lo para fins de registro.
Naturalmente, tal string deve incluir todas as propriedades importantes.
Poderíamos implementar a conversão assim:
deixe usuário = { nome: "João", idade: 30, toString() { retornar `{nome: "${this.name}", idade: ${this.age}}`; } }; alerta(usuário); // {nome: "John", idade: 30}
…Mas no processo de desenvolvimento, novas propriedades são adicionadas, propriedades antigas são renomeadas e removidas. Atualizar esse toString
sempre pode se tornar uma dor. Poderíamos tentar fazer um loop nas propriedades dele, mas e se o objeto for complexo e tiver objetos aninhados nas propriedades? Precisaríamos implementar sua conversão também.
Felizmente, não há necessidade de escrever o código para lidar com tudo isso. A tarefa já foi resolvida.
O JSON (JavaScript Object Notation) é um formato geral para representar valores e objetos. É descrito como no padrão RFC 4627. Inicialmente foi feito para JavaScript, mas muitas outras linguagens possuem bibliotecas para lidar com isso também. Portanto, é fácil usar JSON para troca de dados quando o cliente usa JavaScript e o servidor é escrito em Ruby/PHP/Java/Whatever.
JavaScript fornece métodos:
JSON.stringify
para converter objetos em JSON.
JSON.parse
para converter JSON de volta em um objeto.
Por exemplo, aqui nós JSON.stringify
um aluno:
deixe aluno = { nome: 'João', idade: 30, isAdmin: falso, cursos: ['html', 'css', 'js'], cônjuge: nulo }; deixe json = JSON.stringify(aluno); alerta(tipo de json); // temos uma string! alerta(json); /* Objeto codificado em JSON: { "nome": "João", "idade": 30, "isAdmin": falso, "cursos": ["html", "css", "js"], "cônjuge": nulo } */
O método JSON.stringify(student)
pega o objeto e o converte em uma string.
A string json
resultante é chamada de objeto codificado em JSON ou serializado ou stringificado ou empacotado . Estamos prontos para enviá-lo por transferência ou colocá-lo em um armazenamento de dados simples.
Observe que um objeto codificado em JSON tem várias diferenças importantes em relação ao literal do objeto:
Strings usam aspas duplas. Sem aspas simples ou crases em JSON. Então 'John'
se torna "John"
.
Os nomes das propriedades dos objetos também são colocados entre aspas duplas. Isso é obrigatório. Então age:30
se torna "age":30
.
JSON.stringify
também pode ser aplicado a primitivos.
JSON oferece suporte aos seguintes tipos de dados:
Objetos { ... }
Matrizes [ ... ]
Primitivos:
cordas,
números,
valores booleanos true/false
,
null
.
Por exemplo:
// um número em JSON é apenas um número alerta( JSON.stringify(1) ) // 1 // uma string em JSON ainda é uma string, mas está entre aspas alerta( JSON.stringify('teste') ) // "teste" alerta(JSON.stringify(true)); // verdadeiro alerta( JSON.stringify([1, 2, 3]) ); // [1,2,3]
JSON é uma especificação independente de idioma somente para dados, portanto, algumas propriedades de objeto específicas de JavaScript são ignoradas por JSON.stringify
.
Nomeadamente:
Propriedades da função (métodos).
Chaves e valores simbólicos.
Propriedades que armazenam undefined
.
deixe usuário = { digaOi() { //ignorado alerta("Olá"); }, [Symbol("id")]: 123, // ignorado algo: indefinido // ignorado }; alerta(JSON.stringify(usuário)); // {} (objeto vazio)
Geralmente tudo bem. Se não é isso que queremos, em breve veremos como customizar o processo.
O melhor é que os objetos aninhados são suportados e convertidos automaticamente.
Por exemplo:
deixe o encontro = { título: "Conferência", sala: { número: 23, participantes: ["joão", "ann"] } }; alerta(JSON.stringify(meetup)); /* Toda a estrutura é restringida: { "title":"Conferência", "sala":{"número":23,"participantes":["john","ann"]}, } */
A limitação importante: não deve haver referências circulares.
Por exemplo:
deixe quarto = { número: 23 }; deixe o encontro = { título: "Conferência", participantes: ["joão", "ann"] }; meetup.place = sala; // sala de referências do encontro sala.occupiedBy = encontro; // reunião de referência de sala JSON.stringify(encontro); // Erro: Convertendo estrutura circular para JSON
Aqui, a conversão falha devido à referência circular: room.occupiedBy
faz referência meetup
e meetup.place
faz referência room
:
A sintaxe completa de JSON.stringify
é:
deixe json = JSON.stringify(valor[, substituto, espaço])
valor
Um valor a ser codificado.
substituto
Matriz de propriedades para codificar ou uma function(key, value)
.
espaço
Quantidade de espaço a ser usado para formatação
Na maioria das vezes, JSON.stringify
é usado apenas com o primeiro argumento. Mas se precisarmos ajustar o processo de substituição, como filtrar referências circulares, podemos usar o segundo argumento de JSON.stringify
.
Se passarmos um array de propriedades para ele, apenas essas propriedades serão codificadas.
Por exemplo:
deixe espaço = { número: 23 }; deixe o encontro = { título: "Conferência", participantes: [{nome: "John"}, {nome: "Alice"}], local: sala // sala de referências do encontro }; sala.occupiedBy = encontro; // reunião de referência de sala alert( JSON.stringify(meetup, ['título', 'participantes']) ); // {"title":"Conferência","participantes":[{},{}]}
Aqui provavelmente somos muito rígidos. A lista de propriedades é aplicada a toda a estrutura do objeto. Portanto, os objetos nos participants
estão vazios, porque name
não está na lista.
Vamos incluir na lista todas as propriedades, exceto room.occupiedBy
, que causariam a referência circular:
deixe quarto = { número: 23 }; deixe o encontro = { título: "Conferência", participantes: [{nome: "John"}, {nome: "Alice"}], local: sala // sala de referências do encontro }; sala.occupiedBy = encontro; // reunião de referência de sala alert( JSON.stringify(meetup, ['título', 'participantes', 'local', 'nome', 'número']) ); /* { "title":"Conferência", "participantes":[{"nome":"John"},{"nome":"Alice"}], "lugar":{"número":23} } */
Agora tudo, exceto occupiedBy
é serializado. Mas a lista de propriedades é bastante longa.
Felizmente, podemos usar uma função em vez de um array como replacer
.
A função será chamada para cada par (key, value)
e deverá retornar o valor “substituído”, que será utilizado no lugar do original. Ou undefined
se o valor for ignorado.
No nosso caso, podemos retornar value
“como está” para tudo, exceto occupiedBy
. Para ignorar occupiedBy
, o código abaixo retorna undefined
:
deixe quarto = { número: 23 }; deixe o encontro = { título: "Conferência", participantes: [{nome: "John"}, {nome: "Alice"}], local: sala // sala de referências do encontro }; sala.occupiedBy = encontro; // reunião de referência de sala alerta( JSON.stringify(meetup, function replacer(chave, valor) { alerta(`${chave}: ${valor}`); retornar (chave == 'ocupadoBy') ? indefinido: valor; })); /* pares chave:valor que vêm para substituir: : [objeto Objeto] título: Conferência participantes: [objeto Objeto],[objeto Objeto] 0: [objeto Objeto] nome: João 1: [objeto Objeto] nome: Alice lugar: [objeto Objeto] número: 23 ocupadoBy: [objeto Objeto] */
Observe que a função replacer
obtém todos os pares chave/valor, incluindo objetos aninhados e itens de array. É aplicado recursivamente. O valor this
replacer
interno é o objeto que contém a propriedade atual.
A primeira chamada é especial. É feito usando um “objeto wrapper” especial: {"": meetup}
. Em outras palavras, o primeiro par (key, value)
possui uma chave vazia e o valor é o objeto de destino como um todo. É por isso que a primeira linha é ":[object Object]"
no exemplo acima.
A ideia é fornecer o máximo de energia possível ao replacer
: ele tem a chance de analisar e substituir/pular até mesmo o objeto inteiro, se necessário.
O terceiro argumento de JSON.stringify(value, replacer, space)
é o número de espaços a serem usados para uma formatação bonita.
Anteriormente, todos os objetos stringificados não tinham recuos e espaços extras. Tudo bem se quisermos enviar um objeto através de uma rede. O argumento space
é usado exclusivamente para uma boa saída.
Aqui space = 2
diz ao JavaScript para mostrar objetos aninhados em múltiplas linhas, com recuo de 2 espaços dentro de um objeto:
deixe usuário = { nome: "João", idade: 25, funções: { isAdmin: falso, isEditor: verdadeiro } }; alerta(JSON.stringify(usuário, nulo, 2)); /* recuos de dois espaços: { "nome": "João", "idade": 25, "funções": { "isAdmin": falso, "isEditor": verdadeiro } } */ /* para JSON.stringify(user, null, 4) o resultado seria mais recuado: { "nome": "João", "idade": 25, "funções": { "isAdmin": falso, "isEditor": verdadeiro } } */
O terceiro argumento também pode ser uma string. Nesse caso, a string é usada para recuo em vez de vários espaços.
O parâmetro space
é usado exclusivamente para fins de registro e saída agradável.
Assim como toString
para conversão de string, um objeto pode fornecer o método toJSON
para conversão para JSON. JSON.stringify
o chama automaticamente, se disponível.
Por exemplo:
deixe espaço = { número: 23 }; deixe o encontro = { título: "Conferência", data: nova data (data.UTC (2017, 0, 1)), sala }; alerta(JSON.stringify(meetup)); /* { "title":"Conferência", "data":"2017-01-01T00:00:00.000Z", // (1) "sala": {"número":23} // (2) } */
Aqui podemos ver que a date
(1)
se tornou uma string. Isso ocorre porque todas as datas possuem um método toJSON
integrado que retorna esse tipo de string.
Agora vamos adicionar um toJSON
personalizado para nossa room
de objetos (2)
:
deixe quarto = { número: 23, para JSON() { retorne este.número; } }; deixe o encontro = { título: "Conferência", sala }; alerta(JSON.stringify(sala)); //23 alerta(JSON.stringify(meetup)); /* { "title":"Conferência", "quarto": 23 } */
Como podemos ver, toJSON
é usado tanto para a chamada direta JSON.stringify(room)
quanto quando room
está aninhado em outro objeto codificado.
Para decodificar uma string JSON, precisamos de outro método chamado JSON.parse.
A sintaxe:
deixe valor = JSON.parse(str[, reviver]);
str
String JSON a ser analisada.
reviver
Função opcional(chave,valor) que será chamada para cada par (key, value)
e pode transformar o valor.
Por exemplo:
//array stringificado deixe números = "[0, 1, 2, 3]"; números = JSON.parse(números); alerta(números[1]); //1
Ou para objetos aninhados:
deixe userData = '{ "nome": "John", "idade": 35, "isAdmin": falso, "amigos": [0,1,2,3] }'; deixe usuário = JSON.parse(userData); alerta(usuário.amigos[1]); //1
O JSON pode ser tão complexo quanto necessário, objetos e arrays podem incluir outros objetos e arrays. Mas devem obedecer ao mesmo formato JSON.
Aqui estão erros típicos em JSON escrito à mão (às vezes temos que escrevê-lo para fins de depuração):
deixe json = `{ name: "John", // erro: nome da propriedade sem aspas "surname": 'Smith', // erro: aspas simples no valor (deve ser duplo) 'isAdmin': false // erro: aspas simples na chave (deve ser dupla) "birthday": new Date(2000, 2, 3), // erro: nenhum "novo" é permitido, apenas valores simples "amigos": [0,1,2,3] // aqui está tudo bem }`;
Além disso, JSON não suporta comentários. Adicionar um comentário ao JSON o torna inválido.
Existe outro formato chamado JSON5, que permite chaves, comentários, etc. sem aspas. Mas esta é uma biblioteca independente, não na especificação da linguagem.
O JSON regular é tão rigoroso não porque seus desenvolvedores sejam preguiçosos, mas para permitir implementações fáceis, confiáveis e muito rápidas do algoritmo de análise.
Imagine, recebemos um objeto meetup
restrito do servidor.
Parece assim:
// título: (título do encontro), data: (data do encontro) let str = '{"title":"Conferência","data":"2017-11-30T12:00:00.000Z"}';
…E agora precisamos desserializá -lo, para voltar ao objeto JavaScript.
Vamos fazer isso chamando JSON.parse
:
let str = '{"title":"Conferência","data":"2017-11-30T12:00:00.000Z"}'; deixe meetup = JSON.parse(str); alerta(meetup.date.getDate()); // Erro!
Opa! Um erro!
O valor de meetup.date
é uma string, não um objeto Date
. Como JSON.parse
poderia saber que deveria transformar essa string em um Date
?
Vamos passar para JSON.parse
a função reviver como segundo argumento, que retorna todos os valores “como estão”, mas date
se tornará um Date
:
let str = '{"title":"Conferência","data":"2017-11-30T12:00:00.000Z"}'; deixe meetup = JSON.parse(str, function(chave, valor) { if (chave == 'data') retornar nova data (valor); valor de retorno; }); alerta(meetup.date.getDate()); // agora funciona!
A propósito, isso também funciona para objetos aninhados:
deixe agendar = `{ "encontros": [ {"title":"Conferência","date":"2017-11-30T12:00:00.000Z"}, {"title":"Aniversário","date":"2017-04-18T12:00:00.000Z"} ] }`; agendamento = JSON.parse(agendamento, função(chave, valor) { if (chave == 'data') retornar nova data (valor); valor de retorno; }); alerta(agendamento.meetups[1].date.getDate()); // funciona!
JSON é um formato de dados que possui seu próprio padrão independente e bibliotecas para a maioria das linguagens de programação.
JSON oferece suporte a objetos simples, arrays, strings, números, booleanos e null
.
JavaScript fornece métodos JSON.stringify para serializar em JSON e JSON.parse para ler JSON.
Ambos os métodos suportam funções de transformador para leitura/escrita inteligente.
Se um objeto tiver toJSON
, ele será chamado por JSON.stringify
.
importância: 5
Transforme o user
em JSON e depois leia-o novamente em outra variável.
deixe usuário = { nome: "John Smith", idade: 35 };
deixe usuário = { nome: "John Smith", idade: 35 }; deixe user2 = JSON.parse(JSON.stringify(user));
importância: 5
Em casos simples de referências circulares, podemos excluir uma propriedade ofensiva da serialização pelo seu nome.
Mas às vezes não podemos usar apenas o nome, pois ele pode ser usado tanto em referências circulares quanto em propriedades normais. Assim podemos verificar o imóvel pelo seu valor.
Escreva a função replacer
para restringir tudo, mas remova as propriedades que fazem referência meetup
:
deixe espaço = { número: 23 }; deixe o encontro = { título: "Conferência", ocupadoBy: [{nome: "John"}, {nome: "Alice"}], lugar: quarto }; // referências circulares sala.occupiedBy = encontro; encontro.self = encontro; alerta( JSON.stringify(meetup, function replacer(chave, valor) { /* seu código */ })); /* o resultado deve ser: { "title":"Conferência", "ocupadoBy":[{"nome":"João"},{"nome":"Alice"}], "lugar":{"número":23} } */
deixe espaço = { número: 23 }; deixe o encontro = { título: "Conferência", ocupadoBy: [{nome: "John"}, {nome: "Alice"}], lugar: quarto }; sala.occupiedBy = encontro; encontro.self = encontro; alerta( JSON.stringify(meetup, function replacer(chave, valor) { return (chave! = "" && valor == encontro)? indefinido: valor; })); /* { "title":"Conferência", "ocupadoBy":[{"nome":"João"},{"nome":"Alice"}], "lugar":{"número":23} } */
Aqui também precisamos testar key==""
para excluir a primeira chamada onde é normal que o value
seja meetup
.