La classe `JSON` en JavaScript
Cet article explique la classe JSON en JavaScript.
Nous expliquerons la classe JSON en JavaScript à l'aide d'exemples pratiques.
YouTube Video
La classe JSON en JavaScript
L'objet JSON possède principalement deux méthodes. JSON.stringify() convertit un objet en une chaîne JSON, et JSON.parse() crée un objet à partir d'une chaîne JSON. JSON est un format d’échange de données qui ne peut représenter que certaines valeurs JavaScript. Les fonctions, undefined, Symbol et les références circulaires ne peuvent pas être convertis directement en JSON.
JSON.stringify()
Tout d’abord, voici un exemple basique de conversion d’un objet en chaîne JSON.
1// Convert a JavaScript object to a JSON string.
2const obj = { name: "Alice", age: 30, active: true };
3const jsonString = JSON.stringify(obj);
4console.log(jsonString); // {"name":"Alice","age":30,"active":true}
- Ce code est l’exemple le plus simple de conversion d’un objet ordinaire en chaîne JSON.
JSON.stringifyretourne la chaîne de façon synchrone.
Le deuxième argument de JSON.stringify (replacer)
En spécifiant replacer comme second argument de JSON.stringify, vous pouvez affiner les règles de conversion. Voici un exemple utilisant un tableau pour ne conserver que certaines propriétés.
1// Use a replacer array to include only specific properties during stringification.
2const user = { id: 1, name: "Bob", password: "secret", role: "admin" };
3const safeJson = JSON.stringify(user, ["id", "name", "role"]);
4console.log(safeJson); // {"id":1,"name":"Bob","role":"admin"}
- Dans cet exemple,
passwordest exclu afin d’assurer une sortie de logs sans risque. Ceci est utile lorsque vous souhaitez retirer des informations sensibles pour des raisons de sécurité.
Utilisation de replacer en tant que fonction (pour la conversion et le filtrage des valeurs)
Si vous souhaitez plus de flexibilité, vous pouvez fournir une fonction pour traiter chaque clé et valeur. Voici un exemple permettant de conserver un Date comme une chaîne au format ISO.
1// Use a replacer function to customize serialization.
2const data = { name: "Carol", joined: new Date("2020-01-01T12:00:00Z") };
3const jsonCustom = JSON.stringify(data, (key, value) => {
4 // Convert Date objects to ISO strings explicitly
5 if (this && this[key] instanceof Date) {
6 return this[key].toISOString();
7 }
8 return value;
9});
10console.log(jsonCustom); // {"name":"Carol","joined":"2020-01-01T12:00:00.000Z"}
- Dans une fonction
replacer, la clé et la valeur sont passées, et la valeur retournée est utilisée dans le JSON final.thisfait référence à l’objet parent, il peut donc aussi être utilisé pour des conversions imbriquées.
Les bases de JSON.parse()
JSON.parse convertit une chaîne JSON en objet. Entourez-le d’un try/catch pour pouvoir traiter les JSON invalides.
1// Parse a JSON string into an object and handle parsing errors.
2const jsonText = '{"title":"Example","count":42}';
3try {
4 const parsed = JSON.parse(jsonText);
5 console.log(parsed.title); // Example
6} catch (err) {
7 console.error("Invalid JSON:", err.message);
8}- Si la chaîne est invalide, une exception sera lancée, donc gérez toujours les exceptions lors du traitement de sources externes.
Restauration personnalisée avec reviver (exemple avec une date)
En utilisant le second argument reviver de JSON.parse, vous pouvez restaurer les valeurs. Voici un exemple de conversion d’une date au format chaîne en objet Date.
1// Use a reviver to turn ISO date strings back into Date objects.
2const jsonWithDate = '{"name":"Dana","joined":"2021-06-15T09:00:00.000Z"}';
3const obj = JSON.parse(jsonWithDate, (key, value) => {
4 // A simple check for ISO date-like strings
5 if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/.test(value)) {
6 return new Date(value);
7 }
8 return value;
9});
10console.log(obj.joined instanceof Date); // true
reviverest appelé pour chaque clé, et la valeur retournée est utilisée dans l’objet final. Merci d’effectuer un contrôle strict du format de date selon votre application.
Formatage lisible (‘pretty-print’) et l’argument space
Pour une sortie indentée et lisible, transmettez un nombre ou une chaîne comme troisième argument à JSON.stringify.
1// Pretty-print an object with 2-space indentation for readability.
2const config = { host: "localhost", port: 3000, debug: true };
3const pretty = JSON.stringify(config, null, 2);
4console.log(pretty);
5/* {
6 "host": "localhost",
7 "port": 3000,
8 "debug": true
9} */- Pour un JSON destiné à la lecture humaine (logs, fichiers de configuration), il est utile de spécifier une indentation. Cependant, cela augmente la taille du fichier.
Comment gérer les références circulaires
JSON.stringify lève une TypeError en cas de référence circulaire. Une solution fréquente consiste à créer un replacer utilisant un ensemble seen pour éviter les références circulaires.
1// Safely stringify objects that may contain circular references.
2function safeStringify(value) {
3 const seen = new WeakSet();
4 return JSON.stringify(value, (key, val) => {
5 if (val && typeof val === "object") {
6 if (seen.has(val)) return "[Circular]";
7 seen.add(val);
8 }
9 return val;
10 });
11}
12
13const a = { name: "A" };
14a.self = a;
15console.log(safeStringify(a)); // {"name":"A","self":"[Circular]"}
- En utilisant un
WeakSet, vous pouvez détecter les références circulaires sans provoquer de fuite de mémoire. En cas de référence circulaire, il renvoie"[Circular]". Il est aussi possible d’attribuer un identifiant de référence au lieu de"[Circular]".
Sérialisation personnalisée avec la méthode toJSON
Si vous définissez une méthode toJSON sur un objet, JSON.stringify utilise sa valeur de retour. C’est utile pour intégrer des règles de conversion propres à chaque type.
1// Define toJSON on a class to customize its JSON representation.
2class Point {
3 constructor(x, y) {
4 this.x = x;
5 this.y = y;
6 }
7 toJSON() {
8 // This will be used by JSON.stringify
9 return { x: this.x, y: this.y, type: "Point" };
10 }
11}
12
13const p = new Point(10, 20);
14console.log(JSON.stringify(p)); // {"x":10,"y":20,"type":"Point"}
toJSONpermet de définir des règles de sérialisation au niveau de l’objet, ce qui est plus localisé et intuitif que l’utilisation dereplacer.
Limitations et remarques concernant JSON
Des exemples notables de valeurs non convertibles en JSON sont : undefined, les fonctions, les Symbol, BigInt et les références circulaires. Faites également attention à la précision numérique (nombres entiers importants, arrondis flottants IEEE, etc.).
1// Demonstrate values that can't be represented in JSON.
2const sample = {
3 a: undefined,
4 b: function () {},
5 c: Symbol("s"),
6 d: 9007199254740993n // BigInt (note: JSON.stringify will throw on BigInt)
7};
8// JSON.stringify will throw for BigInt and will drop undefined, functions, symbols in objects.
9try {
10 console.log(JSON.stringify(sample));
11} catch (err) {
12 console.error("Error during stringify:", err.message);
13}- Pour manipuler
BigInt, convertissez-les en chaîne de caractères ou définissez leur représentation via unreplaceroutoJSONpersonnalisé.
Sécurité (en manipulant du JSON non fiable)
JSON.parse en soi est sûr et plus sécurisé que eval, mais il est dangereux de faire confiance à l’objet parsé et de l’utiliser directement pour accéder à des propriétés ou interroger une base de données. Validez toujours et vérifiez que les données correspondent au schéma attendu.
1// Parse external JSON and validate expected properties before use.
2const external = '{"username":"eve","role":"user"}';
3const parsed = JSON.parse(external);
4if (typeof parsed.username === "string" && ["user","admin"].includes(parsed.role)) {
5 console.log("Safe to use parsed.username and parsed.role.");
6} else {
7 throw new Error("Invalid payload");
8}- Pour la validation des entrées (validation de schéma), l’utilisation de bibliothèques comme
ajvest robuste.
Considérations de performance
La (dé)sérialisation fréquente de gros objets sollicite intensément le CPU et la mémoire. Si nécessaire, vous pouvez envisager de n’envoyer que les différences, d’utiliser des formats binaires (type MessagePack), ou le streaming (traitement séquentiel des données). Dans les environnements navigateur, il est parfois possible d’utiliser structuredClone (pour la copie) ou les objets transférables dans postMessage.
Conseils spécifiques (court)
Vous pouvez également envisager les points suivants :.
- Pour les logs, évitez le ‘pretty-print’ et utilisez un JSON sur une seule ligne pour minimiser la taille.
- Les objets fréquemment sérialisés doivent être allégés au préalable, par exemple en retirant les propriétés superflues.
- Utilisez
replacerpour minimiser le parcours et exclure les propriétés inutiles.
Exemple pratique : flux d’envoi/réception d’une requête API
Enfin, voici une séquence montrant comment convertir un objet en JSON de manière sécurisée pour l’envoi à un serveur et restaurer une date à la réception de la réponse.
1// Prepare payload, stringify safely, send via fetch, and revive dates on response.
2async function sendUserUpdate(url, user) {
3 // Remove sensitive info before sending
4 const payload = JSON.stringify(user, (k, v) => (k === "password" ? undefined : v));
5 const res = await fetch(url, {
6 method: "POST",
7 headers: { "Content-Type": "application/json" },
8 body: payload
9 });
10
11 const text = await res.text();
12 // Reviver: convert ISO date strings back to Date
13 const data = JSON.parse(text, (key, value) => {
14 if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/.test(value)) {
15 return new Date(value);
16 }
17 return value;
18 });
19 return data;
20}- Dans cet exemple,
passwordest exclu avant l’envoi, et la date est restaurée parreviveraprès réception. En utilisation réelle, il faudra ajouter la gestion des erreurs et des délais d’attente (timeout).
Résumé
En utilisant JSON.stringify() et JSON.parse(), vous pouvez convertir entre objets et chaînes. Vous pouvez personnaliser le processus de conversion en utilisant replacer et reviver, et si vous implémentez toJSON dans une classe, vous pouvez aussi contrôler individuellement chaque objet. Les références circulaires, BigInt, fonctions, etc. ne peuvent pas être gérés tels quels, il faut donc les traiter en amont.
Validez toujours les données externes, et assurez-vous que les informations sensibles ne figurent pas dans les logs ou les communications. Si la taille des données est importante, il est efficace d’éviter le ‘pretty-print’, d’envisager la transmission différentielle ou d’utiliser des formats binaires.
Vous pouvez suivre l'article ci-dessus avec Visual Studio Code sur notre chaîne YouTube. Veuillez également consulter la chaîne YouTube.