JSON binario: scegliere tra MessagePack, CBOR e BSON
In sintesi: MessagePack, CBOR e BSON comprimono tutti JSON in byte. Confronta il costo in byte, la gestione di int64 e binari, e ottieni un criterio chiaro su quando il cambio vale la pena.

In sintesi: mantieni JSON testuale per lo scambio esterno e per i dati che le persone devono ispezionare, e passa a MessagePack o CBOR sui collegamenti interni solo quando payload binari, interi oltre 2^53 o decimali esatti rendono la dimensione compressa o l'espressività dei tipi un problema reale; BSON appartiene all'ecosistema MongoDB.
JSON testuale risparmia gli spazi; i formati binari risparmiano la punteggiatura e la forma testuale dei numeri. I nomi dei campi compaiono comunque una volta per record, quindi calcola il risparmio reale prima di cambiare.
1. Cosa risparmia davvero il JSON binario
Fissa prima le aspettative. Nessuno di questi formati è "un'altra sintassi JSON". Codificano lo stesso modello di dati in byte: oggetti, array, stringhe, numeri, booleani e null. Ciò che rimuovono è concreto:
- Caratteri strutturali come virgolette, due punti, virgole e graffe;
- La forma testuale decimale dei numeri, così
32diventa un singolo byte; - Il riscansione per trovare i confini delle stringhe, sostituito da un prefisso a lunghezza fissa;
- La codifica Base64 per i payload binari, che possono essere memorizzati direttamente come byte.
Ciò che non possono rimuovere è altrettanto concreto: i nomi dei campi compaiono comunque come stringhe in ogni record. Una mappa MessagePack, una mappa CBOR e un documento BSON ripetono tutti la chiave per ogni voce. Quindi quando il tuo JSON è diecimila record con la stessa forma, il divario di dimensione compressa tra testo e binario è molto più piccolo di quanto suggerisca l'intuizione, perché gzip è bravissimo a eliminare i nomi di campo ripetuti.
2. Da dove vengono i tre formati
| Formato | Specifica e anno | Origine | Uso tipico |
|---|---|---|---|
| MessagePack | Specifica msgpack.org, dal 2008 | Design indipendente orientato al minimalismo | Valori di cache, IPC, pipeline di log |
| CBOR | RFC 8949 (STD 94), 2020 | Influenzato da MessagePack, standardizzato dall'IETF | COSE, WebAuthn, CWT, dispositivi vincolati |
| BSON | bsonspec.org, ecosistema MongoDB | Progettato per i documenti di database | Storage e wire protocol di MongoDB |
CBOR è il più standardizzato: un media type application/cbor registrato IANA, regole di codifica deterministica e CDDL per la descrizione. MessagePack è il più leggero, con una specifica che sta in una pagina. BSON è il più pesante: incorpora ObjectId, Decimal128, date e sottotipi binari nel suo sistema di tipi, ed è per questo che è davvero popolare solo nell'ecosistema MongoDB.
3. Gli stessi dati, quattro codifiche
Prendi {"name":"joe"} come esempio. I costi in byte sono facili da confrontare:
| Codifica | Byte | Sequenza di byte |
|---|---|---|
| Testo JSON | 14 | 7b 22 6e 61 6d 65 22 3a 22 6a 6f 65 22 7d |
| MessagePack | 9 | 81 a3 6e 61 6d 65 a3 6a 6f 65 |
| CBOR | 10 | a1 64 6e 61 6d 65 63 6a 6f 65 |
| BSON | 19 | Lunghezza totale di 4 byte + byte di tipo + name\0 + lunghezza del valore di 4 byte + joe\0 + terminatore |
Scomponi i nove byte di MessagePack:
81: il semibyte alto1000significa mappa, il semibyte basso0001significa una coppia chiave-valore;a3: i tre bit alti101significano una stringa a lunghezza fissa di lunghezza 3;6e 61 6d 65: ASCII pername;a3: un'altra stringa di lunghezza 3;6a 6f 65:joe.
Il a1 di CBOR è il major type 5 (mappa) con lunghezza 1, e 64 è il major type 3 (stringa di testo) con lunghezza 4, quindi la semantica è quasi isomorfa a MessagePack. BSON è più grande: ogni documento porta una lunghezza totale di 4 byte, ogni campo ripete un byte di tipo e il suo nome, e ogni stringa ripete una lunghezza più un \0 finale. Sui documenti piccoli, BSON può facilmente superare il testo JSON.
Puoi controllare i prefissi direttamente renderizzando quei byte in esadecimale:
const messagePackBytes = [0x81, 0xa3, 0x6e, 0x61, 0x6d, 0x65, 0xa3, 0x6a, 0x6f, 0x65];
const hexText = messagePackBytes.map((byte) => byte.toString(16)).join(' ');
console.log(hexText); // 81 a3 6e 61 6d 65 a3 6a 6f 65
3.1 Dimensione e velocità di parsing: ignora i numeri di marketing
Le affermazioni che il binario sia "10 volte più veloce" dovrebbero essere divise in due domande.
Dimensione. Non compresso, il binario è più piccolo perché punteggiatura e spazi sono spariti. Ma le pipeline reali quasi sempre comprimono: dopo gzip o brotli, i nomi di campo ripetuti vengono compressi via, e il vantaggio del binario si riduce drasticamente. Segui il metodo di Minificazione JSON contro gzip: misura prima i byte compressi, poi decidi se un cambio di formato vale la pena.
Velocità di parsing. Il binario salta il lexing (scansionare virgolette, virgole e caratteri numerici), quindi in teoria dovrebbe essere più veloce. In JavaScript, però, il costo dominante è l'allocazione di oggetti e memoria, che nessun decoder può evitare. Forma dei dati, numero di campi e qualità dell'implementazione contano tutti, quindi l'unica conclusione affidabile viene dal benchmark dei tuoi payload, non dal moltiplicatore citato da un fornitore. Se un singolo payload è troppo grande per essere letto in memoria in una volta, affettalo con l'approccio descritto in Parsing e streaming di JSON di grandi dimensioni prima di misurare il guadagno della codifica.
4. int64 e campi binari: la vera ragione per cambiare
La dimensione spesso non è la ragione per cambiare formato. L'espressività dei tipi lo è.
4.1 Interi oltre 2^53
I numeri JSON finiscono in un double in JavaScript, quindi 9007199254740993 diventa 9007199254740992. I tre formati binari gestiscono la cosa diversamente:
| Formato | Interi a 64 bit | Unsigned 64 bit | Precisione arbitraria |
|---|---|---|---|
| MessagePack | ✓ int64 (0xd3), decodificabile in BigInt |
✓ uint64 (0xcf) |
✗ (richiede un tipo di estensione) |
| CBOR | ✓ forme a 64 bit del major type 0/1 | ✓ Stesso spazio di codifica | ✓ tag bignum 2/3 |
| BSON | ✓ int64 (0x12) |
✗ Solo con segno | ✗ (Decimal128 è a virgola fissa, non un intero arbitrario) |
CBOR è l'unico con una rappresentazione standard per gli interi a precisione arbitraria: i valori oltre 64 bit vengono racchiusi nel tag 2 (non negativo) o nel tag 3 (negativo) attorno a una stringa di byte. MessagePack e BSON ripiegano su stringhe o su convenzioni di estensione personalizzate.
4.2 Payload binari
JSON ha esattamente un modo per esprimere il binario: Base64, che gonfia il payload di circa un terzo e richiede una copia di decodifica aggiuntiva. MessagePack ha i tipi bin (0xc4–0xc6), CBOR ha le stringhe di byte (major type 2), e BSON ha i sottotipi binari. Quando trasporti immagini, hash, vettori o blocchi compressi, questa differenza conta molto più del risparmio di qualche byte di punteggiatura.
5. Confronto fianco a fianco
| Dimensione | MessagePack | CBOR | BSON |
|---|---|---|---|
| Specifica | Specifica della comunità | RFC IETF 8949 | Ecosistema MongoDB |
| Media type | application/msgpack (non standard IANA) |
application/cbor (registrato IANA) |
Nessun media type generale |
| Sistema di tipi | Sei tipi JSON più bin ed ext | Sei tipi JSON più bin, tag e valori semplici | Più ampio: ObjectId, Decimal128, date, regex, timestamp |
| Meccanismo di estensione | Tipi di estensione (per esempio timestamp -1) | Tag (date, bignum, CBOR incorporato) | Codici di tipo fissi, estensioni per convenzione dell'ecosistema |
| Codifica deterministica | Nessun requisito standard | Regole esplicite, utilizzabili per le firme | Nessuna |
| Autodescrittivo | Sì, ma senza schema a livello di campo | Sì, e descrivibile con CDDL | Sì, con i nomi di campo ripetuti per documento |
| Overhead sui documenti piccoli | Basso | Basso | Alto (prefisso di lunghezza, nomi di campo, terminatore) |
| Ecosistema | Cache, log, alcuni RPC | COSE, WebAuthn, IoT | Solo MongoDB |
6. Supporto nel browser e in Node
Node 24 non include nessuno di questi codec, quindi installi una libreria. Scelte comuni:
| Runtime | MessagePack | CBOR | BSON |
|---|---|---|---|
| Node.js | @msgpack/msgpack, msgpackr |
cbor-x, borc |
bson |
| Browser | @msgpack/msgpack |
build browser di cbor-x, cbor-web |
bson |
Entrambe le famiglie funzionano su Uint8Array e non richiedono Buffer. Attenzione alle differenze di versione:
import { encode, decode } from '@msgpack/msgpack';
/**
* Encodes an object to MessagePack bytes and decodes it immediately for verification.
* @param payload Plain object to encode
* @returns The decoded value and the encoded byte length
*/
function roundTrip(payload: Record<string, unknown>): { decoded: unknown; byteLength: number } {
const bytes = encode(payload);
return { decoded: decode(bytes), byteLength: bytes.byteLength };
}
La maggior parte delle librerie decodifica per impostazione predefinita i numeri grandi in number e perde precisione oltre l'intervallo sicuro. Se i valori esatti contano, attiva esplicitamente l'opzione BigInt (useBigInt64 in @msgpack/msgpack, un interruttore equivalente in cbor-x), altrimenti hai solo spostato il vecchio problema di JSON in un nuovo formato.
7. Debuggabilità e compatibilità del protocollo
7.1 Debuggabilità: il più grande costo nascosto
Il più grande vantaggio del JSON testuale non è la dimensione; è che puoi aprirlo in qualsiasi momento. Dopo il passaggio al binario:
grep,awkejqsmettono tutti di funzionare, e le pipeline di log richiedono un passaggio di decodifica aggiuntivo;- Il debug in produzione richiede il decoder giusto alla versione giusta, altrimenti i nomi dei campi che vedi saranno sbagliati;
- Il traffico catturato appare negli strumenti per sviluppatori del browser come un blob di byte illeggibile;
- I diff di code review mostrano una sequenza di esadecimali.
CBOR ha la notazione diagnostica, che riporta i byte in testo leggibile vicino a JSON, per esempio una mappa come {1: 2}; anche MessagePack ha alcuni decoder da riga di comando. Ma tutti richiedono un'installazione aggiuntiva e restano fuori dal percorso operativo standard.
La conclusione: ogni volta che le persone devono ispezionare frequentemente i dati, il costo nascosto di un formato binario supera la banda che risparmia.
7.2 Compatibilità del protocollo ed estensioni
- CBOR ha il meccanismo di tag più completo: tag 0 per una stringa date-ora, tag 1 per un timestamp Unix, tag 2 e 3 per i bignum, e tag 24 per CBOR incorporato. Insieme alla codifica deterministica, è usato da protocolli che richiedono firme e coerenza tra implementazioni, come COSE, WebAuthn e CWT.
- MessagePack lascia i tipi di estensione agli implementatori. In pratica l'unico comune è timestamp (tipo di estensione -1). Tra team diversi, ogni tipo di estensione deve essere scritto nel documento del protocollo, altrimenti l'altra parte non può decodificarlo.
- BSON ha codici di tipo fissi, e
ObjectId,Decimal128,MinKeyeMaxKeysono semantiche specifiche di MongoDB. Trattare BSON come formato di scambio generale porta con sé una pila di tipi inutilizzati e prefissi di lunghezza inutilizzati.
Se un protocollo richiede che "gli stessi dati producano gli stessi byte", solo CBOR fornisce una risposta standard; MessagePack e BSON richiedono entrambi un accordo aggiuntivo su ordine dei campi e dettagli di codifica.
8. Quando vale la pena cambiare
Segnali che giustificano il cambio:
- I payload sono grandi, entrambe le estremità possono cambiare, e le dimensioni compresse differiscono ancora in modo significativo;
- Trasporti contenuti binari e l'inflazione Base64 e il costo di decodifica sono misurabili;
- Ti servono interi a 64 bit o decimali esatti che i double di JSON non possono rappresentare;
- Il collegamento è server-to-server e gli strumenti diagnostici possono essere standardizzati.
Segnali che lo sconsigliano:
- gzip o brotli è già in uso e il guadagno è a una cifra percentuale;
- L'API è pubblica e le terze parti hanno solo client HTTP generici;
- I dati sono log o configurazioni che le persone leggono a mano;
- Il team non ha budget per mantenere decoder e compatibilità di versione.
Funziona anche una via di mezzo: mantieni JSON all'esterno e usa MessagePack per le cache interne. I valori in cache sono letti solo dai tuoi servizi, quindi sia la dimensione sia il costo di parsing migliorano, e svuotare la cache risolve qualsiasi problema senza toccare il contratto.
9. Checklist
- [ ] Confronta i conteggi compressi di byte da dati reali, non i conteggi non compressi;
- [ ] Decidi la politica per gli interi grandi: opzione BigInt, trasporto come stringa o bignum CBOR;
- [ ] Invia i campi binari come bin o stringhe di byte invece di racchiuderli in Base64;
- [ ] Scrivi tipi di estensione e tag nel documento del protocollo e fissa le versioni dei decoder;
- [ ] Mantieni un percorso di debug in JSON testuale che possa essere abilitato durante gli incidenti in produzione;
- [ ] Esegui un benchmark e registra numeri riproducibili invece di affermazioni verbali.
Qual è la differenza tra MessagePack e CBOR?
Le loro codifiche sono molto simili: mappe, array e stringhe usano tutti un compatto prefisso tipo-più-lunghezza. La differenza principale è la standardizzazione. CBOR è un RFC IETF con media type IANA, meccanismo di tag e regole di codifica deterministica; MessagePack è una specifica della comunità i cui tipi di estensione si basano su accordi privati. Scegli CBOR quando ti servono firme, coerenza tra implementazioni o protocolli per dispositivi vincolati.
BSON può sostituire JSON come formato generale?
Non è consigliato. BSON è stato progettato per i documenti di database: ogni documento porta una lunghezza totale di 4 byte, ogni campo ripete tipo e nome, e i documenti piccoli finiscono per essere più grandi del testo JSON. Include anche tipi specifici di MongoDB come ObjectId e MinKey. Fuori da MongoDB, la maggior parte del suo sistema di tipi è inutilizzata mentre la penalità di dimensione resta.
Di quanto riduce il payload un formato binario?
Dipende dalla forma dei dati. Quando i nomi dei campi si ripetono molto, JSON testuale compresso e formati binari finiscono vicini; quando i nomi sono corti e i dati sono pieni di caratteri strutturali e numeri, il vantaggio del binario è più evidente. Misura campioni reali sia non compressi sia dopo gzip, poi decidi, invece di stimare con un moltiplicatore generico.
Posso ancora fare debug di dati binari con jq?
No. jq elabora solo JSON testuale e fallirà immediatamente sui byte MessagePack o CBOR. Devi prima decodificare con la libreria corrispondente in JSON e poi passarlo a jq; CBOR può anche passare per uno strumento di notazione diagnostica per diventare testo leggibile. Mantenere un percorso di debug testuale costa meno che installare strumenti su richiesta.