Digamos que tenemos un objeto complejo y nos gustaría convertirlo en una cadena, enviarlo a través de una red o simplemente generarlo para fines de registro.
Naturalmente, dicha cadena debería incluir todas las propiedades importantes.
Podríamos implementar la conversión de esta manera:
dejar usuario = { nombre: "Juan", edad: 30, toString() { return `{nombre: "${this.name}", edad: ${this.age}}`; } }; alerta(usuario); // {nombre: "Juan", edad: 30}
…Pero en el proceso de desarrollo, se agregan nuevas propiedades, se cambia el nombre de las antiguas y se eliminan. Actualizar dicho toString
cada vez puede convertirse en una molestia. Podríamos intentar recorrer las propiedades que contiene, pero ¿qué pasa si el objeto es complejo y tiene objetos anidados en las propiedades? También necesitaríamos implementar su conversión.
Afortunadamente, no es necesario escribir código para manejar todo esto. La tarea ya ha sido resuelta.
El JSON (JavaScript Object Notation) es un formato general para representar valores y objetos. Se describe como en el estándar RFC 4627. Inicialmente fue creado para JavaScript, pero muchos otros lenguajes también tienen bibliotecas para manejarlo. Por lo tanto, es fácil usar JSON para el intercambio de datos cuando el cliente usa JavaScript y el servidor está escrito en Ruby/PHP/Java/lo que sea.
JavaScript proporciona métodos:
JSON.stringify
para convertir objetos a JSON.
JSON.parse
para convertir JSON nuevamente en un objeto.
Por ejemplo, aquí utilizamos JSON.stringify
a un estudiante:
dejar estudiante = { nombre: 'Juan', edad: 30, isAdmin: falso, cursos: ['html', 'css', 'js'], cónyuge: nulo }; let json = JSON.stringify(estudiante); alerta (tipo de json); // ¡Tenemos una cuerda! alerta(json); /* Objeto codificado en JSON: { "nombre": "Juan", "edad": 30, "isAdmin": falso, "cursos": ["html", "css", "js"], "cónyuge": nulo } */
El método JSON.stringify(student)
toma el objeto y lo convierte en una cadena.
La cadena json
resultante se denomina objeto codificado en JSON , serializado , encadenado o ordenado . Estamos listos para enviarlo por cable o colocarlo en un almacén de datos simple.
Tenga en cuenta que un objeto codificado en JSON tiene varias diferencias importantes con respecto al objeto literal:
Las cadenas utilizan comillas dobles. Sin comillas simples ni acentos graves en JSON. Entonces 'John'
se convierte en "John"
.
Los nombres de las propiedades de los objetos también están entre comillas dobles. Eso es obligatorio. Entonces age:30
se convierte en "age":30
.
JSON.stringify
también se puede aplicar a primitivas.
JSON admite los siguientes tipos de datos:
Objetos { ... }
Matrices [ ... ]
Primitivos:
instrumentos de cuerda,
números,
valores booleanos true/false
,
null
.
Por ejemplo:
// un número en JSON es solo un número alerta( JSON.stringify(1) ) // 1 // una cadena en JSON sigue siendo una cadena, pero entre comillas dobles alerta( JSON.stringify('prueba') ) // "prueba" alerta (JSON.stringify (verdadero)); // verdadero alerta( JSON.stringify([1, 2, 3]) ); // [1,2,3]
JSON es una especificación independiente del lenguaje de solo datos, por lo que JSON.stringify
omite algunas propiedades de objetos específicas de JavaScript.
A saber:
Propiedades de funciones (métodos).
Claves y valores simbólicos.
Propiedades que almacenan undefined
.
dejar usuario = { decir Hola() { // ignorado alerta("Hola"); }, [Símbolo("id")]: 123, // ignorado algo: indefinido // ignorado }; alerta (JSON.stringify (usuario)); // {} (objeto vacío)
Normalmente eso está bien. Si eso no es lo que queremos, pronto veremos cómo personalizar el proceso.
Lo bueno es que los objetos anidados se admiten y se convierten automáticamente.
Por ejemplo:
dejar que nos reunamos = { título: "Conferencia", habitación: { número: 23, participantes: ["john", "ana"] } }; alerta (JSON.stringify (reunión)); /* Toda la estructura está encadenada: { "title":"Conferencia", "sala":{"número":23,"participantes":["john","ann"]}, } */
La limitación importante: no debe haber referencias circulares.
Por ejemplo:
dejar habitación = { número: 23 }; dejar que nos reunamos = { título: "Conferencia", participantes: ["john", "ana"] }; encuentro.lugar = habitación; // sala de referencias de reuniones room.occupiedBy = reunión; // reunión de referencias de sala JSON.stringify(reunión); // Error: convertir estructura circular a JSON
Aquí, la conversión falla debido a una referencia circular: room.occupiedBy
hace referencia meetup
y meetup.place
hace referencia room
:
La sintaxis completa de JSON.stringify
es:
let json = JSON.stringify(valor[, sustituto, espacio])
valor
Un valor para codificar.
sustituto
Matriz de propiedades para codificar o una function(key, value)
.
espacio
Cantidad de espacio a utilizar para formatear
La mayoría de las veces, JSON.stringify
se utiliza sólo con el primer argumento. Pero si necesitamos ajustar el proceso de reemplazo, como filtrar referencias circulares, podemos usar el segundo argumento de JSON.stringify
.
Si le pasamos una matriz de propiedades, solo se codificarán estas propiedades.
Por ejemplo:
dejar habitación = { número: 23 }; dejar que nos reunamos = { título: "Conferencia", participantes: [{nombre: "John"}, {nombre: "Alice"}], lugar: sala // sala de referencias de reuniones }; room.occupiedBy = reunión; // reunión de referencias de sala alerta (JSON.stringify (reunión, ['título', 'participantes'])); // {"title":"Conferencia","participantes":[{},{}]}
Probablemente seamos demasiado estrictos en este punto. La lista de propiedades se aplica a toda la estructura del objeto. Entonces los objetos en participants
están vacíos porque name
no está en la lista.
Incluyamos en la lista todas las propiedades excepto room.occupiedBy
que causaría la referencia circular:
dejar habitación = { número: 23 }; dejar que nos reunamos = { título: "Conferencia", participantes: [{nombre: "John"}, {nombre: "Alice"}], lugar: sala // sala de referencias de reuniones }; room.occupiedBy = reunión; // reunión de referencias de sala alert( JSON.stringify(meetup, ['título', 'participantes', 'lugar', 'nombre', 'número']) ); /* { "title":"Conferencia", "participantes":[{"nombre":"Juan"},{"nombre":"Alicia"}], "lugar":{"número":23} } */
Ahora todo, excepto occupiedBy
está serializado. Pero la lista de propiedades es bastante larga.
Afortunadamente, podemos usar una función en lugar de una matriz como replacer
.
La función se llamará para cada par (key, value)
y debería devolver el valor "reemplazado", que se utilizará en lugar del original. O undefined
si se va a omitir el valor.
En nuestro caso, podemos devolver value
"tal cual" para todo excepto occupiedBy
. Para ignorar occupiedBy
, el siguiente código devuelve undefined
:
dejar habitación = { número: 23 }; dejar que nos reunamos = { título: "Conferencia", participantes: [{nombre: "John"}, {nombre: "Alice"}], lugar: sala // sala de referencias de reuniones }; room.occupiedBy = reunión; // reunión de referencias de sala alerta (JSON.stringify (reunión, reemplazo de función (clave, valor) { alerta(`${clave}: ${valor}`); return (clave == 'ocupado por')? indefinido: valor; })); /* clave:pares de valores que vienen al sustituto: : [objeto Objeto] título: Conferencia participantes: [objeto Objeto],[objeto Objeto] 0: [objeto Objeto] nombre: Juan 1: [objeto Objeto] nombre: alicia lugar: [objeto Objeto] número: 23 ocupado por: [objeto objeto] */
Tenga en cuenta que la función replacer
obtiene cada par clave/valor, incluidos los objetos anidados y los elementos de la matriz. Se aplica de forma recursiva. El valor de this
replacer
interno es el objeto que contiene la propiedad actual.
La primera convocatoria es especial. Se elabora utilizando un “objeto contenedor” especial: {"": meetup}
. En otras palabras, el primer par (key, value)
tiene una clave vacía y el valor es el objeto de destino en su conjunto. Es por eso que la primera línea es ":[object Object]"
en el ejemplo anterior.
La idea es proporcionar tanta potencia como sea posible al replacer
: tiene la posibilidad de analizar y reemplazar/omitir incluso todo el objeto si es necesario.
El tercer argumento de JSON.stringify(value, replacer, space)
es la cantidad de espacios que se utilizarán para un formato bonito.
Anteriormente, todos los objetos encadenados no tenían sangrías ni espacios adicionales. Eso está bien si queremos enviar un objeto a través de una red. El argumento space
se utiliza exclusivamente para obtener un buen resultado.
Aquí space = 2
le dice a JavaScript que muestre objetos anidados en varias líneas, con una sangría de 2 espacios dentro de un objeto:
dejar usuario = { nombre: "Juan", edad: 25, roles: { isAdmin: falso, esEditor: verdadero } }; alerta(JSON.stringify(usuario, nulo, 2)); /* sangrías de dos espacios: { "nombre": "Juan", "edad": 25, "roles": { "isAdmin": falso, "isEditor": verdadero } } */ /* para JSON.stringify(user, null, 4) el resultado tendría más sangría: { "nombre": "Juan", "edad": 25, "roles": { "isAdmin": falso, "isEditor": verdadero } } */
El tercer argumento también puede ser una cadena. En este caso, la cadena se utiliza para sangría en lugar de varios espacios.
El parámetro space
se utiliza únicamente para fines de registro y resultados agradables.
Al igual que toString
para la conversión de cadenas, un objeto puede proporcionar el método toJSON
para la conversión a JSON. JSON.stringify
lo llama automáticamente si está disponible.
Por ejemplo:
dejar habitación = { número: 23 }; dejar que nos reunamos = { título: "Conferencia", fecha: nueva fecha (Fecha.UTC (2017, 0, 1)), habitación }; alerta (JSON.stringify (reunión)); /* { "title":"Conferencia", "fecha":"2017-01-01T00:00:00.000Z", // (1) "habitación": {"número":23} // (2) } */
Aquí podemos ver que la date
(1)
se convirtió en una cadena. Esto se debe a que todas las fechas tienen un método toJSON
incorporado que devuelve ese tipo de cadena.
Ahora agreguemos un toJSON
personalizado para nuestra room
de objetos (2)
:
dejar habitación = { número: 23, a JSON() { devolver este número; } }; dejar que nos reunamos = { título: "Conferencia", habitación }; alerta (JSON.stringify (habitación)); // 23 alerta (JSON.stringify (reunión)); /* { "title":"Conferencia", "habitación": 23 } */
Como podemos ver, toJSON
se usa tanto para la llamada directa JSON.stringify(room)
como cuando room
está anidado en otro objeto codificado.
Para decodificar una cadena JSON, necesitamos otro método llamado JSON.parse.
La sintaxis:
let valor = JSON.parse(str[, reviver]);
cadena
Cadena JSON para analizar.
vivificador
Función opcional (clave, valor) que se llamará para cada par (key, value)
y puede transformar el valor.
Por ejemplo:
// matriz encadenada let números = "[0, 1, 2, 3]"; números = JSON.parse(números); alerta( números[1] ); // 1
O para objetos anidados:
let userData = '{ "nombre": "John", "edad": 35, "isAdmin": false, "amigos": [0,1,2,3] }'; dejar usuario = JSON.parse(userData); alerta( usuario.amigos[1] ); // 1
El JSON puede ser tan complejo como sea necesario, los objetos y arrays pueden incluir otros objetos y arrays. Pero deben obedecer al mismo formato JSON.
A continuación se muestran errores típicos en JSON escrito a mano (a veces tenemos que escribirlo con fines de depuración):
dejar json = `{ nombre: "John", // error: nombre de propiedad sin comillas "apellido": 'Smith', // error: comillas simples en el valor (deben ser dobles) 'isAdmin': falso // error: comillas simples en la clave (deben ser dobles) "cumpleaños": nueva fecha (2000, 2, 3), // error: no se permite "nuevo", solo valores básicos "amigos": [0,1,2,3] // aquí todo bien }`;
Además, JSON no admite comentarios. Agregar un comentario a JSON lo invalida.
Hay otro formato llamado JSON5, que permite claves sin comillas, comentarios, etc. Pero esta es una biblioteca independiente, no está en la especificación del lenguaje.
El JSON normal es así de estricto no porque sus desarrolladores sean vagos, sino para permitir implementaciones fáciles, confiables y muy rápidas del algoritmo de análisis.
Imagínese, obtuvimos un objeto meetup
encadenado del servidor.
Se parece a esto:
// título: (título de la reunión), fecha: (fecha de la reunión) let str = '{"title":"Conferencia","date":"2017-11-30T12:00:00.000Z"}';
…Y ahora necesitamos deserializarlo , para volver a convertirlo en un objeto JavaScript.
Hagámoslo llamando JSON.parse
:
let str = '{"title":"Conferencia","date":"2017-11-30T12:00:00.000Z"}'; dejar reunirse = JSON.parse(str); alerta (meetup.date.getDate()); // ¡Error!
¡Vaya! ¡Un error!
El valor de meetup.date
es una cadena, no un objeto Date
. ¿Cómo podría saber JSON.parse
que debería transformar esa cadena en una Date
?
Pasemos a JSON.parse
la función de reactivación como segundo argumento, que devuelve todos los valores "tal cual", pero date
se convertirá en una Date
:
let str = '{"title":"Conferencia","date":"2017-11-30T12:00:00.000Z"}'; let meetup = JSON.parse(cadena, función(clave, valor) { if (clave == 'fecha') devuelve nueva Fecha (valor); valor de retorno; }); alerta (meetup.date.getDate()); // ¡ahora funciona!
Por cierto, eso también funciona para objetos anidados:
dejar horario = `{ "reuniones": [ {"title":"Conferencia","date":"2017-11-30T12:00:00.000Z"}, {"title":"Cumpleaños","fecha":"2017-04-18T12:00:00.000Z"} ] }`; programación = JSON.parse(programación, función (clave, valor) { if (clave == 'fecha') devuelve nueva Fecha (valor); valor de retorno; }); alerta (programar.reuniones[1].fecha.getDate()); // ¡obras!
JSON es un formato de datos que tiene su propio estándar independiente y bibliotecas para la mayoría de los lenguajes de programación.
JSON admite objetos simples, matrices, cadenas, números, booleanos y null
.
JavaScript proporciona los métodos JSON.stringify para serializar en JSON y JSON.parse para leer desde JSON.
Ambos métodos admiten funciones de transformador para lectura/escritura inteligentes.
Si un objeto tiene toJSON
, entonces lo llama JSON.stringify
.
importancia: 5
Convierta al user
en JSON y luego léalo nuevamente en otra variable.
dejar usuario = { nombre: "John Smith", edad: 35 };
dejar usuario = { nombre: "John Smith", edad: 35 }; let usuario2 = JSON.parse(JSON.stringify(usuario));
importancia: 5
En casos simples de referencias circulares, podemos excluir una propiedad infractora de la serialización por su nombre.
Pero a veces no podemos usar simplemente el nombre, ya que puede usarse tanto en referencias circulares como en propiedades normales. Entonces podemos verificar la propiedad por su valor.
Escriba una función replacer
para encadenar todo, pero elimine las propiedades que hacen referencia meetup
:
dejar habitación = { número: 23 }; dejar que nos reunamos = { título: "Conferencia", ocupado por: [{nombre: "John"}, {nombre: "Alice"}], lugar: habitación }; // referencias circulares room.occupiedBy = reunión; encuentro.self = encuentro; alerta (JSON.stringify (reunión, reemplazo de función (clave, valor) { /* tu código */ })); /* el resultado debería ser: { "title":"Conferencia", "ocupadoPor":[{"nombre":"Juan"},{"nombre":"Alicia"}], "lugar":{"número":23} } */
dejar habitación = { número: 23 }; dejar que nos reunamos = { título: "Conferencia", ocupado por: [{nombre: "John"}, {nombre: "Alice"}], lugar: habitación }; room.occupiedBy = reunión; encuentro.self = encuentro; alerta (JSON.stringify (reunión, reemplazo de función (clave, valor) { retorno (clave! = "" && valor == reunión)? indefinido: valor; })); /* { "title":"Conferencia", "ocupadoPor":[{"nombre":"Juan"},{"nombre":"Alicia"}], "lugar":{"número":23} } */
Aquí también necesitamos probar key==""
para excluir la primera llamada donde es normal que el value
sea meetup
.