Table of Contents
JSON en JavaScript : Guide Complet de parse, stringify et Transformations Avancees
Introduction
JSON (JavaScript Object Notation) est le format d’echange de donnees le plus utilise dans le developpement web moderne. Leger, lisible par les humains et facilement analysable par les machines, JSON est devenu le standard de facto pour la communication entre clients et serveurs.
Que vous construisiez une API REST, stockiez des configurations, ou communiquiez avec des services tiers, vous manipulez quotidiennement du JSON. JavaScript offre deux methodes natives puissantes pour travailler avec ce format : JSON.parse() et JSON.stringify().
Pourquoi JSON est-il si populaire ?
JSON presente plusieurs avantages qui expliquent son adoption massive :
- Simplicite : La syntaxe est intuitive et proche des objets JavaScript
- Interoperabilite : Supporte par tous les langages de programmation modernes
- Legerete : Moins verbeux que XML, reduisant la bande passante
- Typage natif : Supporte les types primitifs (string, number, boolean, null, array, object)
Cas d’usage courants
// Communication avec une API REST
const response = await fetch('/api/users');
const users = await response.json(); // Utilise JSON.parse() en interne
// Stockage local dans le navigateur
localStorage.setItem('preferences', JSON.stringify(userPrefs));
const savedPrefs = JSON.parse(localStorage.getItem('preferences'));
// Configuration d'applications
const config = JSON.parse(fs.readFileSync('config.json', 'utf8'));
Dans cet article complet, nous allons explorer en profondeur les methodes JSON.parse() et JSON.stringify(), leurs fonctions avancees (reviver et replacer), et les techniques pour gerer les cas complexes comme les dates, les objets cycliques et les classes personnalisees.
JSON.parse() : Convertir une Chaine en Objet
La methode JSON.parse() analyse une chaine de caracteres JSON et construit la valeur JavaScript ou l’objet correspondant.
Syntaxe de base
JSON.parse(text)
JSON.parse(text, reviver)
Parametres :
text: La chaine JSON a analyserreviver(optionnel) : Une fonction de transformation appelee pour chaque paire cle/valeur
Exemples simples
// Parsing d'un objet simple
const jsonString = '{"name": "Alice", "age": 30, "active": true}';
const user = JSON.parse(jsonString);
console.log(user.name); // "Alice"
console.log(typeof user.age); // "number"
// Parsing d'un tableau
const arrayJson = '[1, 2, 3, "quatre", null]';
const arr = JSON.parse(arrayJson);
console.log(arr.length); // 5
// Parsing de valeurs primitives
JSON.parse('42'); // 42
JSON.parse('"hello"'); // "hello"
JSON.parse('true'); // true
JSON.parse('null'); // null
La fonction reviver : Transformation au parsing
La fonction reviver est un outil puissant qui permet de transformer les valeurs pendant le parsing. Elle est appelee pour chaque paire cle/valeur, de la plus profonde a la racine.
Signature de la fonction reviver
function reviver(key, value) {
// key : la cle de la propriete (chaine vide pour la racine)
// value : la valeur associee
// this : l'objet contenant la propriete
return value; // La valeur transformee
}
Exemple : Mise en majuscules des noms
const jsonString = '[{"name":"John","score":51},{"name":"Jack","score":17}]';
const data = JSON.parse(jsonString, function reviver(key, value) {
if (key === 'name') {
return value.toUpperCase();
}
return value;
});
console.log(data);
// [{ name: "JOHN", score: 51 }, { name: "JACK", score: 17 }]
Exemple : Conversion automatique des dates
Un probleme frequent avec JSON est la perte du type Date. Les dates sont serialisees en chaines ISO 8601 :
const jsonWithDate = '{"created": "2025-01-15T10:30:00.000Z", "name": "Event"}';
// Sans reviver : created est une chaine
const withoutReviver = JSON.parse(jsonWithDate);
console.log(typeof withoutReviver.created); // "string"
// Avec reviver : conversion automatique en Date
const isoDatePattern = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/;
const withReviver = JSON.parse(jsonWithDate, (key, value) => {
if (typeof value === 'string' && isoDatePattern.test(value)) {
return new Date(value);
}
return value;
});
console.log(withReviver.created instanceof Date); // true
console.log(withReviver.created.getFullYear()); // 2025
Exemple : Reconstruction de classes personnalisees
class User {
constructor(name, email) {
this.name = name;
this.email = email;
}
greet() {
return `Bonjour, je suis ${this.name}`;
}
static fromJSON(json) {
return new User(json.name, json.email);
}
}
const jsonString = '{"$type": "User", "name": "Alice", "email": "alice@example.com"}';
const reviver = (key, value) => {
if (value && value.$type === 'User') {
return User.fromJSON(value);
}
return value;
};
const user = JSON.parse(jsonString, reviver);
console.log(user.greet()); // "Bonjour, je suis Alice"
console.log(user instanceof User); // true
Gestion des erreurs de parsing
JSON.parse() lance une SyntaxError si la chaine JSON est invalide. Il est essentiel de toujours entourer les appels avec un try/catch :
function safeJsonParse(jsonString, defaultValue = null) {
try {
return JSON.parse(jsonString);
} catch (error) {
console.error('Erreur de parsing JSON:', error.message);
return defaultValue;
}
}
// Utilisation
const validJson = '{"name": "Test"}';
const invalidJson = '{name: "Test"}'; // Guillemets manquants
console.log(safeJsonParse(validJson)); // { name: "Test" }
console.log(safeJsonParse(invalidJson, {})); // {} (valeur par defaut)
Validation avant parsing
Pour une validation plus robuste, vous pouvez verifier la structure apres parsing :
function parseAndValidate(jsonString, schema) {
try {
const parsed = JSON.parse(jsonString);
// Validation basique du schema
for (const [key, type] of Object.entries(schema)) {
if (typeof parsed[key] !== type) {
throw new Error(`Propriete "${key}" doit etre de type "${type}"`);
}
}
return { success: true, data: parsed };
} catch (error) {
return { success: false, error: error.message };
}
}
// Utilisation
const result = parseAndValidate(
'{"name": "Alice", "age": 30}',
{ name: 'string', age: 'number' }
);
if (result.success) {
console.log('Donnees valides:', result.data);
} else {
console.error('Validation echouee:', result.error);
}
JSON.stringify() : Convertir un Objet en Chaine
La methode JSON.stringify() convertit une valeur JavaScript en chaine JSON.
Syntaxe complete
JSON.stringify(value)
JSON.stringify(value, replacer)
JSON.stringify(value, replacer, space)
Parametres :
value: La valeur a convertirreplacer(optionnel) : Fonction de transformation ou tableau de proprietes a inclurespace(optionnel) : Indentation pour le formatage (nombre ou chaine)
Exemples simples
// Objet simple
const user = { name: 'Bob', age: 25 };
console.log(JSON.stringify(user));
// '{"name":"Bob","age":25}'
// Tableau
const numbers = [1, 2, 3];
console.log(JSON.stringify(numbers));
// '[1,2,3]'
// Valeurs primitives
console.log(JSON.stringify('hello')); // '"hello"'
console.log(JSON.stringify(42)); // '42'
console.log(JSON.stringify(true)); // 'true'
console.log(JSON.stringify(null)); // 'null'
La fonction replacer : Filtrage et transformation
La fonction replacer permet de controler quelles proprietes sont incluses et comment elles sont transformees.
Signature de la fonction replacer
function replacer(key, value) {
// key : la cle de la propriete
// value : la valeur associee
// this : l'objet contenant la propriete
// Retourner undefined pour exclure la propriete
return value;
}
Exemple : Filtrer des proprietes sensibles
const user = {
name: 'Alice',
email: 'alice@example.com',
password: 'secret123',
apiKey: 'sk-abc123',
role: 'admin'
};
const safeReplacer = (key, value) => {
// Exclure les proprietes sensibles
const sensitiveKeys = ['password', 'apiKey', 'secret'];
if (sensitiveKeys.includes(key)) {
return undefined; // Exclut la propriete
}
return value;
};
console.log(JSON.stringify(user, safeReplacer, 2));
// {
// "name": "Alice",
// "email": "alice@example.com",
// "role": "admin"
// }
Exemple : Transformer les valeurs
const userRecords = [
{ name: 'Joe', points: 14.9, level: 31.5 },
{ name: 'Jane', points: 22.3, level: 45.7 }
];
const anonymizedReport = JSON.stringify(userRecords, (key, value) => {
if (key === 'name') {
return undefined; // Supprimer les noms (anonymisation)
}
if (typeof value === 'number') {
return Math.floor(value); // Arrondir les nombres
}
return value;
}, 2);
console.log(anonymizedReport);
// [
// { "points": 14, "level": 31 },
// { "points": 22, "level": 45 }
// ]
Utiliser un tableau comme replacer
Au lieu d’une fonction, vous pouvez passer un tableau de noms de proprietes a inclure :
const user = {
id: 1,
name: 'Alice',
email: 'alice@example.com',
password: 'secret',
createdAt: '2025-01-15'
};
// Inclure uniquement certaines proprietes
const publicJson = JSON.stringify(user, ['id', 'name', 'email']);
console.log(publicJson);
// '{"id":1,"name":"Alice","email":"alice@example.com"}'
Le parametre space : Formatage lisible
Le troisieme parametre permet d’ajouter une indentation pour rendre le JSON lisible :
const data = {
name: 'Project',
version: '1.0.0',
dependencies: {
lodash: '^4.17.21',
axios: '^1.6.0'
}
};
// Indentation avec 2 espaces
console.log(JSON.stringify(data, null, 2));
// {
// "name": "Project",
// "version": "1.0.0",
// "dependencies": {
// "lodash": "^4.17.21",
// "axios": "^1.6.0"
// }
// }
// Indentation avec tabulation
console.log(JSON.stringify(data, null, '\t'));
// Indentation personnalisee
console.log(JSON.stringify(data, null, '----'));
La methode toJSON() : Serialisation personnalisee
Les objets peuvent definir une methode toJSON() pour controler leur serialisation :
class User {
constructor(name, email, password) {
this.name = name;
this.email = email;
this.password = password;
this.createdAt = new Date();
}
// Methode appelee automatiquement par JSON.stringify()
toJSON() {
return {
$type: 'User',
name: this.name,
email: this.email,
// password est volontairement omis
createdAt: this.createdAt.toISOString()
};
}
}
const user = new User('Alice', 'alice@example.com', 'secret123');
console.log(JSON.stringify(user, null, 2));
// {
// "$type": "User",
// "name": "Alice",
// "email": "alice@example.com",
// "createdAt": "2025-01-15T10:30:00.000Z"
// }
Cas d’Usage Avances
Conversion des dates : Le probleme et les solutions
Les objets Date sont automatiquement convertis en chaines ISO 8601 par JSON.stringify(), mais JSON.parse() ne les reconvertit pas automatiquement :
const event = {
title: 'Conference',
date: new Date('2025-06-15T14:00:00Z')
};
// Serialisation
const json = JSON.stringify(event);
console.log(json);
// '{"title":"Conference","date":"2025-06-15T14:00:00.000Z"}'
// Parsing sans reviver : date reste une chaine
const parsed = JSON.parse(json);
console.log(typeof parsed.date); // "string"
console.log(parsed.date.getMonth); // undefined
Solution : Reviver universel pour les dates
function dateReviver(key, value) {
// Pattern ISO 8601 complet
const isoPattern = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z?$/;
if (typeof value === 'string' && isoPattern.test(value)) {
const date = new Date(value);
// Verifier que la date est valide
if (!isNaN(date.getTime())) {
return date;
}
}
return value;
}
const parsed = JSON.parse(json, dateReviver);
console.log(parsed.date instanceof Date); // true
console.log(parsed.date.toLocaleDateString()); // "15/06/2025"
Deep Clone avec JSON : Avantages et limites
Une technique populaire pour cloner des objets est d’utiliser JSON :
// Clone profond simple
const original = {
name: 'Config',
settings: {
theme: 'dark',
notifications: true
},
items: [1, 2, { nested: 'value' }]
};
const clone = JSON.parse(JSON.stringify(original));
// Verification : modifications independantes
clone.settings.theme = 'light';
console.log(original.settings.theme); // "dark" (non affecte)
Limitations du clone JSON :
const problematic = {
date: new Date(), // Devient une chaine
func: () => 'hello', // Perdu (undefined)
undef: undefined, // Perdu
symbol: Symbol('test'), // Perdu
infinity: Infinity, // Devient null
nan: NaN, // Devient null
regex: /pattern/gi, // Devient un objet vide {}
map: new Map([['a', 1]]), // Devient un objet vide {}
set: new Set([1, 2, 3]) // Devient un objet vide {}
};
const cloned = JSON.parse(JSON.stringify(problematic));
console.log(cloned);
// {
// date: "2025-01-15T...", // Chaine au lieu de Date
// infinity: null,
// nan: null,
// regex: {},
// map: {},
// set: {}
// }
// func, undef, symbol sont absents
Alternative moderne : structuredClone()
// Disponible dans les navigateurs modernes et Node.js 17+
const original = {
date: new Date(),
map: new Map([['key', 'value']]),
set: new Set([1, 2, 3])
};
const clone = structuredClone(original);
console.log(clone.date instanceof Date); // true
console.log(clone.map instanceof Map); // true
Objets cycliques : Le probleme et les solutions
JSON ne supporte pas les references circulaires :
const world = {
name: 'World',
regions: []
};
const europe = {
name: 'Europe',
parent: world // Reference circulaire !
};
world.regions.push(europe);
// ERREUR : TypeError: Converting circular structure to JSON
JSON.stringify(world);
Solution 1 : Detecter et remplacer les cycles
function stringifyWithCycles(obj) {
const seen = new WeakSet();
return JSON.stringify(obj, (key, value) => {
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) {
return '[Circular Reference]';
}
seen.add(value);
}
return value;
}, 2);
}
console.log(stringifyWithCycles(world));
// {
// "name": "World",
// "regions": [
// {
// "name": "Europe",
// "parent": "[Circular Reference]"
// }
// ]
// }
Solution 2 : Utiliser des identifiants de reference
function stringifyWithRefs(obj) {
const refs = new Map();
let refId = 0;
// Premier passage : identifier les objets
JSON.stringify(obj, (key, value) => {
if (typeof value === 'object' && value !== null) {
if (!refs.has(value)) {
refs.set(value, `$ref:${refId++}`);
}
}
return value;
});
// Second passage : remplacer les cycles
const seen = new WeakSet();
return JSON.stringify(obj, (key, value) => {
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) {
return { $ref: refs.get(value) };
}
seen.add(value);
}
return value;
}, 2);
}
Classes personnalisees avec $type
Pour serialiser et deserialiser des instances de classes :
// Definition des classes
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toJSON() {
return { $type: 'Point', x: this.x, y: this.y };
}
static fromJSON(data) {
return new Point(data.x, data.y);
}
distance(other) {
return Math.sqrt((this.x - other.x) ** 2 + (this.y - other.y) ** 2);
}
}
class Rectangle {
constructor(topLeft, bottomRight) {
this.topLeft = topLeft;
this.bottomRight = bottomRight;
}
toJSON() {
return {
$type: 'Rectangle',
topLeft: this.topLeft,
bottomRight: this.bottomRight
};
}
static fromJSON(data, reviver) {
return new Rectangle(
reviver('topLeft', data.topLeft),
reviver('bottomRight', data.bottomRight)
);
}
get area() {
const width = Math.abs(this.bottomRight.x - this.topLeft.x);
const height = Math.abs(this.bottomRight.y - this.topLeft.y);
return width * height;
}
}
// Registre des classes
const classRegistry = {
Point: Point,
Rectangle: Rectangle
};
// Reviver universel
function classReviver(key, value) {
if (value && value.$type && classRegistry[value.$type]) {
const ClassRef = classRegistry[value.$type];
return ClassRef.fromJSON(value, classReviver);
}
return value;
}
// Utilisation
const rect = new Rectangle(new Point(0, 0), new Point(10, 5));
const json = JSON.stringify(rect, null, 2);
console.log(json);
const restored = JSON.parse(json, classReviver);
console.log(restored instanceof Rectangle); // true
console.log(restored.topLeft instanceof Point); // true
console.log(restored.area); // 50
Bonnes Pratiques
1. Toujours utiliser try/catch avec JSON.parse()
// MAL
const data = JSON.parse(untrustedInput); // Peut lancer une exception
// BIEN
let data;
try {
data = JSON.parse(untrustedInput);
} catch (e) {
console.error('JSON invalide:', e.message);
data = defaultValue;
}
2. Valider les donnees apres parsing
Ne faites jamais confiance aux donnees JSON provenant de sources externes :
const parsed = JSON.parse(apiResponse);
// Valider la structure attendue
if (!parsed || typeof parsed.id !== 'number' || typeof parsed.name !== 'string') {
throw new Error('Format de reponse invalide');
}
3. Utiliser le parametre space en developpement uniquement
// En developpement : lisible
console.log(JSON.stringify(data, null, 2));
// En production : compact (economie de bande passante)
const payload = JSON.stringify(data);
4. Definir toJSON() pour les classes complexes
class ApiResponse {
constructor(data, meta) {
this.data = data;
this.meta = meta;
this._internalCache = {}; // Propriete privee
}
toJSON() {
// Exclure les proprietes internes
return {
data: this.data,
meta: this.meta
};
}
}
5. Creer des helpers reutilisables
const JSONUtils = {
safeParse(json, defaultValue = null) {
try {
return JSON.parse(json);
} catch {
return defaultValue;
}
},
prettyPrint(obj) {
return JSON.stringify(obj, null, 2);
},
parseWithDates(json) {
const isoPattern = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/;
return JSON.parse(json, (key, value) => {
if (typeof value === 'string' && isoPattern.test(value)) {
return new Date(value);
}
return value;
});
}
};
6. Documenter les formats JSON de votre API
/**
* Format de reponse utilisateur
* @typedef {Object} UserResponse
* @property {number} id - ID unique
* @property {string} name - Nom complet
* @property {string} email - Email
* @property {string} createdAt - Date ISO 8601
*/
Pieges Courants
1. undefined est ignore silencieusement
const obj = {
name: 'Test',
value: undefined,
items: [1, undefined, 3]
};
console.log(JSON.stringify(obj));
// '{"name":"Test","items":[1,null,3]}'
// value est absent, undefined dans le tableau devient null
2. Les fonctions sont perdues
const obj = {
name: 'Calculator',
add: (a, b) => a + b,
multiply: function(a, b) { return a * b; }
};
console.log(JSON.stringify(obj));
// '{"name":"Calculator"}'
// Les fonctions sont completement ignorees
3. NaN et Infinity deviennent null
const numbers = {
valid: 42,
notANumber: NaN,
infinite: Infinity,
negInfinite: -Infinity
};
console.log(JSON.stringify(numbers));
// '{"valid":42,"notANumber":null,"infinite":null,"negInfinite":null}'
4. Les proprietes Symbol sont ignorees
const sym = Symbol('secret');
const obj = {
name: 'Public',
[sym]: 'Hidden value'
};
console.log(JSON.stringify(obj));
// '{"name":"Public"}'
// La propriete Symbol est absente
5. Les Map et Set deviennent des objets vides
const data = {
map: new Map([['key', 'value']]),
set: new Set([1, 2, 3])
};
console.log(JSON.stringify(data));
// '{"map":{},"set":{}}'
// Solution : convertir avant serialisation
const serializable = {
map: Object.fromEntries(data.map),
set: Array.from(data.set)
};
console.log(JSON.stringify(serializable));
// '{"map":{"key":"value"},"set":[1,2,3]}'
6. Les BigInt lancent une erreur
const obj = { bigNumber: 9007199254740993n };
// TypeError: Do not know how to serialize a BigInt
JSON.stringify(obj);
// Solution : definir toJSON sur BigInt.prototype (deconseille)
// ou utiliser un replacer
const json = JSON.stringify(obj, (key, value) => {
if (typeof value === 'bigint') {
return value.toString() + 'n';
}
return value;
});
Conclusion
Les methodes JSON.parse() et JSON.stringify() sont des outils fondamentaux en JavaScript moderne. Leur maitrise est essentielle pour tout developpeur travaillant avec des APIs, du stockage local, ou des configurations.
Tableau Recapitulatif
| Methode | Usage | Options Cles |
|---|---|---|
JSON.parse(text) | Convertir JSON en objet | - |
JSON.parse(text, reviver) | Parsing avec transformation | reviver(key, value) |
JSON.stringify(value) | Convertir objet en JSON | - |
JSON.stringify(value, replacer) | Serialisation avec filtrage | replacer(key, value) ou ['prop1', 'prop2'] |
JSON.stringify(value, null, space) | JSON formate | space : nombre ou chaine |
Points Cles a Retenir
- Toujours gerer les erreurs avec
JSON.parse()dans un bloc try/catch - Les dates necessitent un reviver pour etre reconverties en objets
Date - Le replacer filtre les proprietes sensibles (mots de passe, cles API)
- Les objets cycliques necessitent un traitement special
- Definir
toJSON()sur vos classes pour controler leur serialisation - Attention aux valeurs non supportees :
undefined, fonctions,Symbol,NaN,Infinity,Map,Set,BigInt
En appliquant ces techniques et bonnes pratiques, vous pourrez manipuler les donnees JSON de maniere robuste et efficace dans tous vos projets JavaScript.
In-Article Ad
Dev Mode
Tags
Mahmoud DEVO
Senior Full-Stack Developer
I'm a passionate full-stack developer with 10+ years of experience building scalable web applications. I write about Vue.js, Node.js, PostgreSQL, and modern DevOps practices.
Enjoyed this article?
Subscribe to get more tech content delivered to your inbox.
Related Articles
Manipulation des dates en JavaScript : UTC, conversion et formatage
Guide complet sur les dates JavaScript : conversion en chaine, creation de dates UTC, methodes setUTC et bonnes pratiques pour eviter les problemes de fuseaux.
La comparaison équivalente en JavaScript : une analyse appro
Here's a compelling meta description that summarizes the main value proposition, includes a subtle call-to-action, and meets the 150-160 character requirement:
Analyse des Dependances JavaScript : Guide npm audit et CI/CD
Securisez vos projets JS avec npm audit. Detectez les vulnerabilites, corrigez-les et integrez l'analyse dans votre pipeline GitHub Actions.