preloader

Métodos JSON, toJSON

Supongamos que tenemos un objeto complejo y queremos convertirlo en una cadena de texto para enviarlo por la red o simplemente para visualizarlo para fines de registro.

Naturalmente, dicha cadena debe incluir todas las propiedades importantes.

Podríamos implementar la conversión de esta manera:

				
					let user = {
  name: "John",
  age: 30,

  toString() {
    return `{name: "${this.name}", age: ${this.age}}`;
  }
};

alert(user); // {name: "John", age: 30}

				
			

Sin embargo, a medida que desarrollamos el código, se pueden añadir nuevas propiedades, o renombrar y eliminar otras. Actualizar el método toString cada vez que esto ocurre puede volverse tedioso. Podríamos intentar recorrer las propiedades, pero ¿qué pasa si el objeto es complejo y tiene objetos anidados? Necesitaríamos implementar su conversión también.

Afortunadamente, no es necesario escribir todo el código para manejar esto. Esta tarea ya ha sido resuelta.

JSON.stringify

JSON (JavaScript Object Notation) es un formato universal para representar valores y objetos. Es un estándar definido en el RFC 4627. Originalmente fue creado para JavaScript, pero muchos otros lenguajes también tienen librerías para manejarlo. Por lo tanto, es fácil usar JSON para intercambiar información entre un cliente que utiliza JavaScript y un servidor escrito en Ruby, PHP, Java, etc.

JavaScript proporciona dos métodos:

  • JSON.stringify para convertir objetos a JSON.
  • JSON.parse para convertir JSON de vuelta a un objeto.

Por ejemplo, aquí usamos JSON.stringify en un objeto student:

				
					let student = {
  name: 'John',
  age: 30,
  isAdmin: false,
  courses: ['html', 'css', 'js'],
  spouse: null
};

let json = JSON.stringify(student);

alert(typeof json); // ¡obtenemos una cadena de texto!

alert(json);
/* Objeto codificado en JSON:
{
  "name": "John",
  "age": 30,
  "isAdmin": false,
  "courses": ["html", "css", "js"],
  "spouse": null
}
*/

				
			

El método JSON.stringify(student) toma el objeto y lo convierte en una cadena de texto.

La cadena JSON resultante se denomina objeto JSON codificado o serializado. Estamos listos para enviarlo por la red o almacenarlo.

Ten en cuenta que el objeto JSON codificado tiene varias diferencias importantes con el objeto literal:

  • Las cadenas utilizan comillas dobles. No hay comillas simples o acentos en JSON. Por lo tanto, 'John' se convierte en "John".
  • Los nombres de las propiedades del objeto también llevan comillas dobles. Esto es obligatorio. Así, age:30 se convierte en "age":30.

JSON.stringify también se puede aplicar a tipos de datos primitivos.

JSON soporta los siguientes tipos de datos:

  • Objetos { ... }
  • Arreglos [ ... ]
  • Primitivos:
    • Cadenas de texto,
    • Números,
    • Valores booleanos true/false,
    • null.

Por ejemplo:

				
					// un número en JSON es sólo un número
alert( JSON.stringify(1) ); // 1

// una cadena en JSON sigue siendo una cadena de texto, pero con comillas dobles
alert( JSON.stringify('test') ); // "test"

alert( JSON.stringify(true) ); // true

alert( JSON.stringify([1, 2, 3]) ); // [1,2,3]

				
			

JSON es una especificación de datos independiente del lenguaje, por lo que algunas propiedades específicas de JavaScript se omiten al usar JSON.stringify.

Estas son:

  • Propiedades de funciones (métodos).
  • Propiedades simbólicas.
  • Propiedades que contienen undefined.
				
					let user = {
  sayHi() { // ignorado
    alert("Hello");
  },
  [Symbol("id")]: 123, // ignorado
  something: undefined // ignorado
};

alert( JSON.stringify(user) ); // {} (objeto vacío)

				
			

Normalmente esto está bien. Si no es lo que queremos, pronto veremos cómo personalizar el proceso.

Lo mejor es que se permiten objetos anidados y se convierten automáticamente.

Por ejemplo:

				
					let meetup = {
  title: "Conference",
  room: {
    number: 23,
    participants: ["john", "ann"]
  }
};

alert( JSON.stringify(meetup) );
/* La estructura completa se convierte en cadena:
{
  "title":"Conference",
  "room":{"number":23,"participants":["john","ann"]}
}
*/

				
			

Una limitación importante: no deben existir referencias circulares.

Por ejemplo:

				
					let room = {
  number: 23
};

let meetup = {
  title: "Conference",
  participants: ["john", "ann"]
};

meetup.place = room;       // meetup hace referencia a room
room.occupiedBy = meetup; // room hace referencia a meetup

JSON.stringify(meetup); // Error: Convirtiendo estructura circular a JSON

				
			

Aquí, la conversión falla debido a una referencia circular: room.occupiedBy hace referencia a meetup, y meetup.place hace referencia a room.

Excluyendo y transformando: replacer

La sintaxis completa de JSON.stringify es:

				
					let json = JSON.stringify(value[, replacer, space])

				
			
  • value: Un valor para codificar.
  • replacer: Un array de propiedades para codificar o una función de mapeo function(propiedad, valor).
  • space: La cantidad de espacio para usar para el formateo.

La mayor parte del tiempo, JSON.stringify se usa solo con el primer argumento. Pero si necesitamos ajustar el proceso de sustitución, como para filtrar las referencias circulares, podemos usar el segundo argumento de JSON.stringify.

Si pasamos un array de propiedades a él, solo esas propiedades serán codificadas.

Por ejemplo:

				
					let room = {
  number: 23
};

let meetup = {
  title: "Conference",
  participants: [{name: "John"}, {name: "Alice"}],
  place: room // meetup hace referencia a room
};

room.occupiedBy = meetup; // room hace referencia a meetup

alert( JSON.stringify(meetup, ['title', 'participants']) );
// {"title":"Conference","participants":[{},{}]}

				
			

Aquí probablemente seamos demasiado estrictos. La lista de propiedades se aplica a toda la estructura del objeto. Por lo tanto, 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 para evitar la referencia circular:

				
					let room = {
  number: 23
};

let meetup = {
  title: "Conference",
  participants: [{name: "John"}, {name: "Alice"}],
  place: room // meetup hace referencia a room
};

room.occupiedBy = meetup; // room hace referencia a meetup

alert( JSON.stringify(meetup, ['title', 'participants', 'place', 'name', 'number']) );
/*
{
  "title":"Conference",
  "participants":[{"name":"John"},{"name":"Alice"}],
  "place":{"number":23}
}
*/

				
			

Ahora todo, excepto occupiedBy, está serializado. Pero la lista de propiedades es bastante larga.

Afortunadamente, podemos usar una función en lugar de un array como replacer.

La función se llamará para cada par de (propiedad, valor) y debe devolver el valor “sustituido”, que se utilizará en lugar del original, o undefined si el valor va a ser omitido.

En nuestro caso, podemos devolver value “tal cual” para todo excepto occupiedBy. Para ignorar occupiedBy, el código de abajo devuelve undefined:

				
					let room = {
  number: 23
};

let meetup = {
  title: "Conference",
  participants: [{name: "John"}, {name: "Alice"}],
  place: room // meetup hace referencia a room
};

room.occupiedBy = meetup; // room hace referencia a meetup

alert( JSON.stringify(meetup, function replacer(key, value) {
  alert(`${key}: ${value}`);
  return (key == 'occupiedBy') ? undefined : value;
}));

/* pares de propiedad:valor que llegan a replacer:
:             [object Object]
title:        Conference
participants: [object Object],[object Object]
0:            [object Object]
name:         John
1:            [object Object]
name:         Alice
place:        [object Object]
number:       23
occupiedBy: [object Object]
*/

				
			

Ten en cuenta que la función replacer recibe todos los pares de propiedad/valor, incluyendo objetos anidados y elementos de array. Se aplica recursivamente. El valor de this dentro de replacer es el objeto que contiene la propiedad actual.

El primer llamado es especial. Se realiza utilizando un “Objeto contenedor” especial: {"": meetup}. En otras palabras, el primer par (propiedad, valor) tiene una propiedad vacía, y el valor es el objeto objetivo como un todo. Por esto, la primera línea es ":[object Object]" en el ejemplo anterior.

La idea es proporcionar tanta capacidad para replacer como sea posible: tiene una oportunidad de analizar y reemplazar/omitir incluso el objeto completo si es necesario.

Formateo: space

El tercer argumento de JSON.stringify(value[, replacer, space]) es el número de espacios de sangría para formateo.

Anteriormente, todos los resultados de JSON.stringify eran de una línea. Eso es bueno si queremos enviar un objeto sobre la red. Pero para logueo y formateo de salida, es más conveniente que se vea bien.

Eso es fácil de hacer con el argumento space:

				
					let user = {
  name: "John",
  age: 25,
  roles: {
    isAdmin: false,
    isEditor: true
  }
};

alert(JSON.stringify(user, null, 2));
/* dos espacios de sangría:
{
  "name": "John",
  "age": 25,
  "roles": {
    "isAdmin": false,
    "isEditor": true
  }
}
*/

alert(JSON.stringify(user, null, 4));
/* cuatro espacios de sangría:
{
    "name": "John",
    "age": 25,
    "roles": {
        "isAdmin": false,
        "isEditor": true
    }
}
*/

				
			

La llamada JSON.stringify(user, null, 4) tiene más espacio en la salida.

Objeto.toJSON

Como alternativa al replacer, podemos proporcionar un método toJSON para todo el objeto o para una propiedad específica.

Cuando JSON.stringify llama a este método, debe devolver un valor que se usará en lugar del objeto original.

Por ejemplo:

				
					let room = {
  number: 23,
  toJSON() {
    return this.number;
  }
};

let meetup = {
  title: "Conference",
  room
};

alert( JSON.stringify(room) ); // 23

alert( JSON.stringify(meetup) );
/* El objeto meetup se convierte a:
{
  "title":"Conference",
  "room": 23
}
*/

				
			

Como puedes ver, toJSON es llamado tanto para el objeto directo JSON.stringify(room) como para los objetos anidados.

Aquí la estructura de datos es simple, pero puede ser más compleja. De cualquier manera, debemos devolver un valor “plano” para la representación JSON.

Recapitulación

  • JSON.stringify convierte objetos en cadenas JSON para enviarlas a través de la red u otros propósitos.
  • JSON.parse convierte las cadenas JSON de nuevo en objetos.
  • Ambos métodos pueden aplicar transformaciones y controlar el proceso de conversión.

Related Post

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *