Parsare JSON in JavaScript è qualcosa che farai centinaia di volte come sviluppatore web. Per fortuna, JavaScript ha il supporto integrato — nessuna libreria necessaria. Ma c'è più sfumatura in JSON.parse() e JSON.stringify() di quanto la documentazione lasci intendere. Vediamolo per bene.

JSON.parse() — Trasformare una Stringa in un Oggetto

JSON.parse() prende una stringa formattata in JSON e la converte in un valore JavaScript. Quel valore è solitamente un oggetto o un array, ma può anche essere una stringa, un numero, un booleano o null:

js
const jsonString = '{"name": "Alice", "age": 30, "active": true}';
const user = JSON.parse(jsonString);

console.log(user.name);    // "Alice"
console.log(user.age);     // 30
console.log(user.active);  // true
console.log(typeof user);  // "object"

Nota che l'input deve essere una stringa. Un errore comune è cercare di parsare un valore che è già un oggetto — JSON.parse({}) genera un SyntaxError perché converte prima l'oggetto in "[object Object]", che non è JSON valido.

Avvolgi Sempre JSON.parse() in un try/catch

Ecco la cosa che nessuno ti dice: JSON.parse() lancia un SyntaxError se l'input non è JSON valido. Se stai parsando dati da un'API, input utente o un file, dovresti sempre gestire quell'errore. Una singola risposta malformata può mandare in crash la tua app se non lo fai:

js
function safeParseJSON(str) {
  try {
    return { data: JSON.parse(str), error: null };
  } catch (err) {
    return { data: null, error: err.message };
  }
}

const { data, error } = safeParseJSON('{"name": "Alice"}');
if (error) {
  console.error('Invalid JSON:', error);
} else {
  console.log(data.name); // "Alice"
}

// Handles bad input gracefully
const result = safeParseJSON('not json at all');
console.log(result.error); // "Unexpected token 'o', "not json "... is not valid JSON"

Uso un wrapper come questo in quasi ogni progetto. Rende la gestione degli errori coerente e impedisce che brutte eccezioni non gestite risalgano fino alla UI.

JSON.stringify() — Trasformare un Oggetto in una Stringa

JSON.stringify() fa il contrario: converte un valore JavaScript in una stringa JSON. Lo userai per inviare dati a un'API, salvare nel localStorage o scrivere su un file:

js
const user = {
  name: "Bob",
  age: 25,
  roles: ["admin", "editor"],
  password: "secret123" // we'll handle this later
};

// Basic usage
const jsonString = JSON.stringify(user);
// '{"name":"Bob","age":25,"roles":["admin","editor"],"password":"secret123"}'

// Pretty-printed (great for logs and file output)
const prettyJson = JSON.stringify(user, null, 2);
console.log(prettyJson);
// {
//   "name": "Bob",
//   "age": 25,
//   "roles": ["admin", "editor"],
//   "password": "secret123"
// }

Il terzo argomento di JSON.stringify() è il livello di indentazione. Usare 2 o 4 ti dà un output leggibile. Il default (nessun terzo argomento) ti dà JSON compatto e minificato — meglio per la trasmissione in rete.

Cosa stringify() Scarta Silenziosamente

Questo mette in difficoltà regolarmente gli sviluppatori. JSON.stringify() omette silenziosamente certi valori perché JSON non li supporta:

js
const data = {
  name: "Alice",
  greet: function() { return "hi"; },   // Functions → dropped
  undef: undefined,                      // undefined → dropped
  sym: Symbol("key"),                    // Symbols → dropped
  nan: NaN,                              // NaN → null
  inf: Infinity,                         // Infinity → null
  date: new Date("2024-01-15")           // Dates → ISO string
};

console.log(JSON.stringify(data, null, 2));
// {
//   "name": "Alice",
//   "nan": null,
//   "inf": null,
//   "date": "2024-01-15T00:00:00.000Z"
// }
// greet, undef, sym are GONE
Attenzione: Se stringifichi un oggetto con valori undefined e poi lo parsi, quelle chiavi saranno completamente assenti. Questo può causare bug sottili se il tuo codice controlla obj.key === undefined in seguito.

Recuperare JSON da un'API — Il Pattern del Mondo Reale

In pratica, la maggior parte del parsing JSON avviene quando recuperi dati da un'API. La Fetch API rende questo semplice — response.json() gestisce il parsing per te:

js
async function getUser(userId) {
  try {
    const response = await fetch(`https://api.example.com/users/${userId}`);

    if (!response.ok) {
      throw new Error(`HTTP error: ${response.status}`);
    }

    const user = await response.json(); // parses JSON automatically
    return user;
  } catch (err) {
    console.error('Failed to fetch user:', err);
    return null;
  }
}

const user = await getUser(42);
if (user) {
  console.log(`Hello, ${user.name}!`);
}

response.json() è essenzialmente JSON.parse(await response.text()). Lancia anche un'eccezione se il corpo della risposta non è JSON valido, quindi vale la pena gestirlo separatamente nel codice in produzione.

Lavorare con Dati JSON Annidati

I JSON profondamente annidati sono comuni nelle API reali. Ecco come navigarli in sicurezza senza andare in crash sulle chiavi mancanti:

js
const apiResponse = {
  "user": {
    "profile": {
      "address": {
        "city": "Berlin"
      }
    }
  }
};

// Unsafe — throws if any level is undefined
const city1 = apiResponse.user.profile.address.city; // "Berlin"

// Safe with optional chaining (ES2020+) — returns undefined instead of throwing
const city2 = apiResponse?.user?.profile?.address?.city; // "Berlin"
const zip   = apiResponse?.user?.profile?.address?.zip;  // undefined (not a crash)

// With a fallback using nullish coalescing
const country = apiResponse?.user?.profile?.address?.country ?? "Unknown";

Il concatenamento opzionale (?.) e il nullish coalescing (??) sono funzionalità JavaScript moderne — entrambe parte della spec ECMAScript dal 2020 — che rendono il lavoro con strutture JSON incerte molto più sicuro. Usali liberamente — sono supportati in tutti i browser moderni e Node.js 14+.

Le Funzioni replacer e reviver

Sia JSON.stringify() che JSON.parse() accettano un secondo argomento che ti permette di personalizzare la trasformazione. Sono genuinamente utili per scenari del mondo reale:

js
// replacer: filter or transform values during stringify
const user = { name: "Alice", password: "s3cr3t", age: 30 };
const safe = JSON.stringify(user, ["name", "age"]); // only include these keys
// '{"name":"Alice","age":30}'  — password is excluded

// reviver: transform values during parse
const dateJson = '{"name": "Alice", "createdAt": "2024-01-15T09:30:00Z"}';
const parsed = JSON.parse(dateJson, (key, value) => {
  if (key === "createdAt") return new Date(value); // convert string to Date
  return value;
});
console.log(parsed.createdAt instanceof Date); // true
console.log(parsed.createdAt.getFullYear());   // 2024

Il pattern reviver è particolarmente utile per ripristinare oggetti Date dopo un ciclo attraverso JSON, dal momento che JSON non ha un tipo data nativo.

Strumenti Utili

Quando lavori con JSON in progetti JavaScript, questi strumenti fanno risparmiare tempo: JSON Formatter per formattare risposte API minificate, JSON Validator per controllare errori di sintassi, JSON Path per interrogare campi specifici da payload di grandi dimensioni, e JSON Escape quando devi incorporare JSON in una stringa. Per maggiore approfondimento, il riferimento MDN JSON è eccellente.

Conclusione

JSON.parse() e JSON.stringify() sono le due funzioni di cui hai bisogno. Le abitudini chiave da costruire: avvolgi sempre parse() in try/catch, usa il concatenamento opzionale per l'accesso annidato, e sappi cosa stringify() scarta silenziosamente. Fai bene queste tre cose e gestirai il 99% degli scenari JSON del mondo reale senza sorprese.