JSON (JavaScript Object Notation) è un formato testuale per lo scambio di dati.
Per tutti questi motivi, è diventato lo standard de facto per lo scambio di dati su Internet, soppiantando XML.
Dichiaratamente da specifica, JSON è basato solamente su due strutture:
I valori possono essere stringhe, numeri, booleani, null, oppure altri oggetti o array.
Non sono ammessi altri tipi.
Attenzione
JSON è un formato di dati, non un linguaggio di programmazione!
Non supporta funzioni, variabili, commenti o qualsiasi altra funzionalità di un linguaggio di programmazione.
Un oggetto è una struttura dati che descrive una sequenza non ordinata di coppie nome-valore.
{ e finisce con parentesi graffa destra }.:, che separano il nome dal valore.
,.A differenza di JavaScript, non possono possedere metodi.
Un array è una struttura dati che descrive una sequenza ordinata di valori.
[ e finisce con parentesi quadra destra ].,.Un valore può essere:
una stringa tra virgolette "
un numero (in formato intero o decimale)
un valore booleano true o false
un valore null,
un oggetto (coppie chiave-valore racchiuse tra {}),
un array (valori ordinati racchiusi tra []).
Non sono supportati i commenti (se non nell’estensione JSONC, poco comune).
Alcune stringhe contengono caratteri speciali che devono essere rappresentati con sequenze di escape:
| Sequenza | Significato |
|---|---|
\n |
Newline (a capo) |
\t |
Tab (tabulazione) |
\r |
Carriage return |
\\ |
Backslash letterale |
\" |
Virgolette doppie |
\/ |
Slash |
\b |
Backspace |
// Stringa con newline
const testo = "Riga 1\nRiga 2\nRiga 3";
console.log(testo);
// Output:
// Riga 1
// Riga 2
// Riga 3
// Stringa con tab
const colonne = "Nome\tCognome\tEta";
console.log(colonne);
// Output: Nome Cognome Eta
// Path con backslash
const path = "C:\\Users\\mario\\file.txt";
console.log(path);
// Output: C:\Users\mario\file.txt
// Virgolette dentro stringa
const citazione = "Mario ha detto \"Ciao!\"";
console.log(citazione);
// Output: Mario ha detto "Ciao!"Gli spazi bianchi (spazi, tabulazioni, newline) sono ignorati e possono essere usati per formattare il JSON.
{"id":"0001","type":"donut","name":"Cake","ppu":0.55,"batters":{"b
atter":[{"id":"1001","type":"Regular"},{"id":"1002","type":"Chocolate
"},{"id":"1003","type":"Blueberry"},{"id":"1004","type":"Devil's
Food"}]},"topping":[{"id":"5001","type":"None"},{"id":"5002","type":
"Glazed"},{"id":"5005","type":"Sugar"},{"id":"5007","type":"Powdere
d Sugar"},{"id":"5006","type":"Chocolate with
Sprinkles"},{"id":"5003","type":"Chocolate"},{"id":"5004","type":"Ma
ple"}]}{
"id": "0001",
"type": "donut",
"name": "Cake",
"ppu": 0.55,
"batters": {
"batter": [
{ "id": "1001", "type": "Regular" },
{ "id": "1002", "type": "Chocolate" },
{ "id": "1003", "type": "Blueberry" },
{ "id": "1004", "type": "Devil's Food" }
]
},
"topping": [
{ "id": "5001", "type": "None" },
{ "id": "5002", "type": "Glazed" },
{ "id": "5005", "type": "Sugar" },
{ "id": "5007", "type": "Powdered Sugar" },
{ "id": "5006", "type": "Chocolate with Sprinkles" },
{ "id": "5003", "type": "Chocolate" },
{ "id": "5004", "type": "Maple" }
]
}Essendo un linguaggio molto sintetico, la leggibilità sta nella capacità di identare il file in modo coerente.
Quando JavaScript si trova a dover scambiare dati (con un server, con localStorage, etc.), è comune usare JSON come formato di serializzazione.
In questi casi, JSON non sarà altro che una stringa.
Abbiamo due metodi globali per convertire tra oggetti JavaScript e stringhe JSON:
JSON.stringify(): converte un oggetto JavaScript in una stringa JSON (serializzazione)JSON.parse: converte una stringa JSON in un oggetto JavaScript (deserializzazione)JSON.stringify: oggetto → stringaJSON.stringify serializza un oggetto JavaScript in una stringa JSON:
JSON.stringify: opzioniLa funzione JSON.stringify ha la seguente firma:
JSON.stringify(value, replacer, space)
value: l’oggetto da serializzarereplacer: (opzionale) array o funzione per filtrare/trasformare i valorispace: (opzionale) stringa o numero per formattare l’output con indentazioneJSON.stringify: opzione replacerL’opzione replacer come array filtra quali proprietà includere:
L’opzione replacer come funzione trasforma i valori:
const utenti = [
{ id: 1, nome: 'Alice', stipendio: 50000 },
{ id: 2, nome: 'Bob', stipendio: 45000 }
];
const json = JSON.stringify(utenti, (key, value) => {
if (key === 'stipendio') {
return '*****';
}
return value;
});
console.log(json);
// '[{"id":1,"nome":"Alice","stipendio":"*****"},{"id":2,"nome":"Bob","stipendio":"*****"}]'JSON.stringify: opzione spaceL’opzione space indenta il JSON per renderlo leggibile:
toJSON()Puoi aggiungere il metodo toJSON() a un oggetto per controllare come viene serializzato:
class Utente {
constructor(id, nome, password) {
this.id = id;
this.nome = nome;
this.password = password; // Non vogliamo serializzare!
}
toJSON() {
return {
id: this.id,
nome: this.nome
// password omessa intenzionalmente
};
}
}
const user = new Utente(1, 'Alice', 'secret');
console.log(JSON.stringify(user));
// '{"id":1,"nome":"Alice"}' ← password non c'è!Serializzare oggetti con riferimenti circolari
Osserva il seguente codice:
Serializza meetup in JSON usando JSON.stringify. Attenzione all’errore che viene generato a causa dei riferimenti circolari!
L’obiettivo è aggirare questo problema e serializzare l’oggetto come segue:
JSON.parse: stringa → oggettoJSON.parse deserializza una stringa JSON in un oggetto JavaScript:
Attenzione: se il JSON non è valido, JSON.parse() genera un errore (SyntaxError)!
JSON.parse: opzioniLa funzione JSON.parse ha la seguente firma:
JSON.parse(text, reviver)
text: la stringa JSON da deserializzarereviver: (opzionale) funzione per trasformare i valori durante il parsingLa funzione reviver viene invocata per ogni coppia e restituisce un value sostitutivo, che verrà inserito al posto di quello originale nell’oggetto durante il parsing:
const jsonString = '{"id":1,"createdAt":"2025-01-15T10:30:00Z"}';
const obj = JSON.parse(jsonString, (key, value) => {
// Converti stringhe ISO in Date objects
if (key === 'createdAt') {
return new Date(value);
}
return value;
});
console.log(obj.createdAt); // Date object, non stringa!
console.log(obj.createdAt instanceof Date); // true// Errore 1: apici singoli (JSON richiede doppi)
JSON.parse("{'id': 1}"); // SyntaxError
// Errore 2: key senza virgolette
JSON.parse('{"id": 1, name: "Alice"}'); // SyntaxError
// Errore 3: undefined non è valido
JSON.parse('{"id": 1, value: undefined}'); // SyntaxError
// Errore 4: stringa non è JSON valido
JSON.parse('{"id": 1, "testo": "ciao'); // SyntaxError (virgola finale manca)
// Corretto
JSON.parse('{"id": 1, "name": "Alice"}'); // OKRicettario persistente con JSON
Estendi il ricettario delle lezioni precedenti per renderlo persistente usando JSON:
JSON.stringify per salvare l’intero array delle ricette in localStorage (chiave: "ricettario").localStorage usando JSON.parse e popola la lista delle ricette in pagina.Fin qui abbiamo usato JSON per serializzare dati localmente (es. in localStorage).
Ma JSON non è nato per questo: è uno standard pensato per lo scambio di dati tra sistemi diversi su rete.
Oggi JSON è il formato dominante nelle API web: ogni volta che un’app carica dati da un server (e.g. una lista di prodotti, il meteo, i tuoi messaggi) molto probabilmente sta ricevendo una risposta in JSON.
Questo scambio avviene su HTTP, il protocollo fondamentale del web. Per capire come funziona davvero, dobbiamo prima capire su quale infrastruttura viaggiano questi dati.
Internet è una rete globale di computer interconnessi che comunicano tramite protocolli standard.
Funzionamento di base:
Un protocollo definisce il formato e l’ordine dei messaggi scambiati tra due entità in comunicazione, e le azioni da intraprendere alla ricezione.
Protocolli umani:
Prevedono messaggi specifici e azioni determinate al ricevimento.
Protocolli di rete:
I protocolli Internet sono pubblicati dall’IETF tramite documenti RFC (Request For Comments).
HTTP (HyperText Transfer Protocol) è il protocollo a livello di applicazione usato nel web.
GET.POST e HEAD.Ogni risorsa su web è identificata da un URL (Uniform Resource Locator).
| Parte | Esempio | Significato |
|---|---|---|
| schema | https:// |
Protocollo usato |
| host | api.example.com:8443 |
Nome del server (hostname + porta) |
| path | /user/42 |
Percorso della risorsa |
| query | ?p1=v1&p2=v2 |
Parametri aggiuntivi (coppie chiave=valore) |
| fragment | #section |
Àncora nella pagina (gestita solo dal browser) |
Entrambe la richiesta e la risposta HTTP condividono una struttura simile:
Il protocollo HTTP implementa diversi metodi, di cui i più utilizzati sono GET e POST.
I metodi HTTP indicano l’azione che il client vuole compiere sulla risorsa identificata dall’URL.
Nonostante siano talvolta usati in modo improprio, i metodi HTTP hanno significati ben definiti e implicano certe proprietà che dovrebbero generalmente essere rispettate:
Inoltre, solamente PUT, POST e PATCH prevedono un body nella richiesta.
| Metodo | Operazione | Safe | Idempotent | Cacheable | Body |
|---|---|---|---|---|---|
GET |
Legge una risorsa | Sì | Sì | Sì | No |
HEAD |
Legge solo gli header | Sì | Sì | Sì | No |
OPTIONS |
Interroga il server | Sì | Sì | No | No |
TRACE |
Esegue loopback | Sì | Sì | No | No |
DELETE |
Elimina una risorsa | No | Sì | No | No |
PUT |
Aggiorna una risorsa | No | Sì | No | Sì |
POST |
Crea una risorsa | No | No | A volte | Sì |
PATCH |
Aggiorna parziale | No | No | A volte | Sì |
CONNECT |
Apre un tunnel col server | No | No | No | No |
I codici di stato in una risposta HTTP comunicano l’esito della richiesta. Sono numeri a tre cifre, dove la prima cifra indica la categoria:
| Categoria | Range | Significato |
|---|---|---|
| Informazione | 1xx | Richiesta ricevuta, elaborazione |
| Successo | 2xx | Richiesta OK |
| Redirezione | 3xx | Risorsa spostata |
| Errore client | 4xx | Errore nella richiesta |
| Errore server | 5xx | Errore del server |
| Status | Messaggio | Significato | Azione tipica |
|---|---|---|---|
200 |
OK | Successo, risorsa restituita | Processa dati |
201 |
Created | Risorsa creata (POST) | Conferma creazione |
204 |
No Content | Successo, nessun body | DELETE riuscito |
400 |
Bad Request | Dati malformati nel body | Valida input |
401 |
Unauthorized | Non autenticato (login richiesto) | Richiedi credenziali |
403 |
Forbidden | Autenticato ma non autorizzato | Mostra accesso negato |
404 |
Not Found | Risorsa non esiste | Mostra errore 404 |
500 |
Internal Server Error | Errore server | Riprova più tardi |
503 |
Service Unavailable | Server offline | Riprova più tardi |
Il browser raccoglie tutte le richieste HTTP che fa per caricare una pagina e le mostra in tempo reale. Lo possiamo osservare dai DevTools:
XMLHttpRequestXMLHttpRequest (abbreviato XHR) è l’API originaria per fare richieste HTTP da JavaScript.
fetch API, più moderne e leggibili.Ha comunque senso conoscerla, sia per capire il funzionamento di base delle richieste HTTP, sia per manutenere codice legacy.
URLPrima di costruire richieste, è utile sapere che JavaScript ha una classe built-in URL per lavorare con gli indirizzi in modo sicuro.
Questo ci è molto utile per costruire URL dinamicamente, ad esempio aggiungendo query parameters:
const url = new URL('https://api.example.com/ricette');
url.searchParams.set('categoria', 'dolci');
url.searchParams.set('q', 'torta al cioccolato');
console.log(url.toString());
// https://api.example.com/ricette?categoria=dolci&q=torta+al+cioccolato
// L'encoding dei caratteri speciali è automatico!
xhr.open('GET', url); // si passa direttamente a open()XMLHttpRequest: APIConfigurazione e invio:
const xhr = new XMLHttpRequest();
xhr.open(method, url); // Configura richiesta (GET, POST, etc.)
xhr.send(body); // Invia la richiesta (body opzionale)
xhr.abort(); // Cancella richiesta in corso
xhr.setRequestHeader(k, v); // Aggiunge un header custom
xhr.timeout = 5000; // Timeout in ms (genera evento 'timeout' se scade)Proprietà della risposta e eventi:
xhr.status; // Codice HTTP (200, 404, 500, ...)
xhr.statusText; // Testo dello status ("OK", "Not Found", ...)
xhr.response; // Body della risposta (nel formato di responseType)
xhr.responseType = 'json'; // Parsing automatico: xhr.response sarà già un oggetto
xhr.onload; // Risposta arrivata (anche con status 4xx/5xx!)
xhr.onerror; // Errore di rete (non status HTTP)
xhr.onprogress; // Durante il download (utile per progress bar)XMLHttpRequest: GET requestEcco come fare una GET request con XHR:
1const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://jsonplaceholder.typicode.com/todos/1');
xhr.responseType = 'json';
2xhr.onload = () => {
if (xhr.status === 200) {
console.log('Todo:', xhr.response.title);
} else {
console.error('Errore HTTP:', xhr.status);
}
};
3xhr.onerror = () => console.error('Errore di rete');
4xhr.send();responseType prima di inviare).
onload: gestisce la risposta, indipendentemente dallo status.
onerror: gestisce errori di rete (non è uno status HTTP).
send(): invia la richiesta (body vuoto).
XMLHttpRequest: POST requestEcco come fare una POST request con XHR per inviare JSON:
1const xhr = new XMLHttpRequest();
xhr.open('POST', 'https://jsonplaceholder.typicode.com/todos');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.responseType = 'json';
2xhr.onload = () => {
if (xhr.status === 201) {
console.log('Nuova todo creata con ID:', xhr.response.id);
} else {
console.error('Errore HTTP:', xhr.status);
}
};
3const body = JSON.stringify({
title: 'Nuovo task',
completed: false,
userId: 1
});
4xhr.send(body);responseType (dopo open, prima di send).
onload: verifica status 201 (Created).
JSON.stringify serializza i dati.
send(body): invia il JSON al server.
Niccolò Maltoni