La classe `JSON` in JavaScript
Questo articolo spiega la classe JSON in JavaScript.
Spiegheremo la classe JSON in JavaScript con esempi pratici.
YouTube Video
La classe JSON in JavaScript
L’oggetto JSON dispone principalmente di due metodi. JSON.stringify() converte un oggetto in una stringa JSON e JSON.parse() crea un oggetto partendo da una stringa JSON. JSON è un formato di scambio dati che può rappresentare solo alcuni valori di JavaScript. Funzioni, undefined, Symbol e riferimenti circolari non possono essere convertiti direttamente in JSON.
JSON.stringify()
Ecco innanzitutto un esempio base di conversione di un oggetto in una stringa 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}
- Questo è l’esempio più semplice di conversione di un oggetto normale in una stringa JSON.
JSON.stringifyrestituisce la stringa in modo sincrono.
Il secondo argomento di JSON.stringify (replacer)
Specificando il replacer come secondo argomento di JSON.stringify, puoi controllare in modo dettagliato le regole di conversione. Ecco un esempio che utilizza un array per mantenere solo determinate proprietà.
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"}
- In questo esempio,
passwordviene esclusa per permettere un output di log sicuro. Questo è utile quando si desidera rimuovere informazioni sensibili per motivi di sicurezza.
Utilizzo di replacer come funzione (per la conversione dei valori e il filtraggio)
Se desideri maggiore flessibilità, puoi fornire una funzione per processare ogni chiave e valore. Qui sotto un esempio di come mantenere una Date come stringa 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"}
- In un
replacerdi tipo funzione, vengono passati chiave e valore e il valore restituito viene usato nel JSON finale.thissi riferisce all’oggetto genitore, quindi può essere utilizzato anche per conversioni annidate.
Nozioni di base su JSON.parse()
JSON.parse converte una stringa JSON in un oggetto. Utilizza try/catch per poter gestire JSON non validi.
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}- Se la stringa non è valida verrà generata un’eccezione, quindi includi sempre la gestione delle eccezioni quando gestisci fonti esterne.
Ripristino personalizzato tramite reviver (esempio di ripristino di oggetti Date)
Utilizzando il secondo argomento reviver di JSON.parse puoi eseguire il ripristino dei valori. Ecco un esempio di conversione di una stringa di data archiviata in un oggetto 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
reviverviene chiamato per ogni chiave e il valore restituito viene usato nell’oggetto finale. Esegui controlli rigorosi sul formato della data in base alla tua applicazione.
Formattazione leggibile e argomento space
Per generare un output indentato e più leggibile, passa un numero o una stringa come terzo argomento a 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} */- Per i JSON destinati ad essere letti dalle persone, come log o file di configurazione, è utile specificare l’indentazione. Tieni però presente che ciò aumenta la dimensione.
Come gestire i riferimenti circolari
JSON.stringify genera un TypeError in caso di riferimento circolare. Una soluzione comune è creare un replacer utilizzando un insieme seen per evitare i riferimenti circolari.
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]"}
- Usando un
WeakSet, puoi rilevare riferimenti circolari evitando perdite di memoria. Nel caso di un riferimento circolare, viene restituito"[Circular]". È anche possibile assegnare un ID di riferimento invece di"[Circular]".
Serializzazione personalizzata con il metodo toJSON
Se definisci un metodo toJSON su un oggetto, JSON.stringify utilizzerà il suo valore di ritorno. Questo è utile quando desideri incorporare regole di conversione per ciascun tipo.
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"}
toJSONti permette di definire regole di serializzazione a livello di oggetto, rendendole più localizzate e intuitive rispetto all’uso direplacer.
Limitazioni e avvertenze di JSON
Esempi notevoli di ciò che non può essere convertito in JSON sono undefined, funzioni, Symbol, BigInt e riferimenti circolari. Presta attenzione anche alla precisione numerica (come grandi interi e arrotondamenti del formato IEEE).
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}- Quando gestisci
BigInt, convertilo in una stringa o definiscine la rappresentazione con unreplacerotoJSONpersonalizzato.
Sicurezza (quando si gestisce JSON non attendibile)
JSON.parse è sicuro e considerato più sicuro di eval, ma è pericoloso fidarsi dell’oggetto ottenuto ed usarlo direttamente per accedere a proprietà o eseguire query sul database. Valida sempre e verifica che i dati rispettino lo schema atteso.
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}- Per la validazione degli input (validazione dello schema), usare librerie come
ajvè una soluzione robusta.
Considerazioni sulle prestazioni
Serializzare e deserializzare frequentemente oggetti di grandi dimensioni grava su CPU e memoria. Se necessario, puoi considerare di inviare solo le differenze, utilizzare formati binari (come MessagePack) o lo streaming (processamento sequenziale dei dati). In ambienti browser, ci sono casi in cui puoi utilizzare structuredClone (per la copia) o oggetti trasferibili in postMessage.
Consigli specifici (brevi)
Puoi anche considerare i seguenti punti:.
- Per i log, evita il pretty-print e usa JSON su una singola riga per ridurre la dimensione.
- Oggetti che vengono serializzati spesso dovrebbero essere resi leggeri in anticipo, ad esempio rimuovendo proprietà non necessarie.
- Usa
replacerper minimizzare l’attraversamento ed escludere proprietà non necessarie.
Esempio pratico: flusso di invio/ricezione richieste API
Infine, ecco una sequenza che mostra come convertire correttamente un oggetto in JSON da inviare al server e come ripristinare una data dalla risposta ricevuta.
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}- In questo esempio, la proprietà
passwordviene esclusa prima dell’invio e la data viene ripristinata tramitereviverdopo la ricezione. Nell’uso reale, sarà necessario aggiungere gestione degli errori e dei timeout.
Riepilogo
Con JSON.stringify() e JSON.parse() puoi convertire tra oggetti e stringhe. Puoi personalizzare il processo di conversione usando replacer e reviver, e se implementi toJSON in una classe, puoi anche controllare ogni oggetto individualmente. Riferimenti circolari, BigInt, funzioni, ecc. non possono essere gestiti così come sono, quindi è necessario elaborarli prima.
Valida sempre l’input esterno e assicurati che informazioni sensibili non finiscano in log o comunicazioni. Se i dati sono di grandi dimensioni, conviene evitare il pretty-print, considerare la trasmissione differenziale o utilizzare formati binari.
Puoi seguire l'articolo sopra utilizzando Visual Studio Code sul nostro canale YouTube. Controlla anche il nostro canale YouTube.