Italiano

Strumenti per sviluppatori · JSON formattatore e validatore

ID interi di grandi dimensioni in JSON: perché i formattatori JavaScript possono arrotondarli

· Perché è importante

json flusso di lavoro dello sviluppatore convalida

ID interi di grandi dimensioni in JSON: perché i formattatori JavaScript possono arrotondarli illustrati con token JSON e un limite di convalida preciso
Illustrazione vettoriale originale ToolAcre

JSON consente numeri interi di qualsiasi dimensione, ma JavaScript rappresenta i numeri come float da 64-bit, quindi qualsiasi cosa sopra 2^53 può cambiare quando viene analizzato e riserializzato. Questo post spiega il limite, come individuare il danno e come proteggere gli ID.

L'ID che è cambiato di uno

L'ID modificato di uno spesso arriva come JSON perfettamente valido. Inserisci `{"orderId":9007199254740993}` in JavaScript e `JSON.parse` restituisce un numero il cui valore visualizzato è `9007199254740992`. L'analisi ha esito positivo perché il token segue la grammatica dei numeri JSON; il danno si verifica durante la conversione di quelle cifre decimali nella rappresentazione numerica di JavaScript. Un formattatore che serializza il valore analizzato scrive fedelmente il Number arrotondato, non il token esatto apparso nell'origine.

Il contrasto è immediato quando vengono citate le stesse cifre. `JSON.parse("{"orderId":"9007199254740993"}")` restituisce la stringa `9007199254740993`, preservando ogni carattere, e `JSON.stringify` restituisce quelle cifre invariate tra virgolette. Questo è il motivo per cui la sola convalida della sintassi non può proteggere un identificatore numerico. Confronta input e output ogni volta che compaiono interi lunghi e tratta gli identificatori come stringhe al confine di produzione quando l'aritmetica non fa parte del loro significato.

Cosa dice RFC 8259 sui numeri

RFC 8259 definisce l'ortografia di un numero JSON ma non fornisce a ogni implementazione un tipo numerico di precisione arbitraria. La grammatica consente un segno meno facoltativo, una parte intera e parti facoltative di frazioni ed esponenti. Sono escluse comodità come la notazione esadecimale, `NaN` e `Infinity`. Di conseguenza, `9007199254740993` è sintatticamente valido anche se un comune consumatore JavaScript non può rappresentare quell'intero esattamente come un numero.

La guida all'interoperabilità della specifica è l'avvertimento pratico: il software utilizza comunemente IEEE 754 numeri binari64 e gli interi nell'intervallo da negativo `2^53 + 1` a positivo `2^53 - 1` sono interoperabili nel senso di accordo esatto. Un validatore può accettare correttamente un token più grande mentre un parser lo arrotonda successivamente.

Da dove proviene 2^53

Il limite `2^53` deriva dalla precisione disponibile in un significato binario64. JavaScript espone il numero intero consecutivamente più alto rappresentabile come `Number.MAX_SAFE_INTEGER`, ovvero `9007199254740991`. A e al di sotto di tale grandezza, gli interi adiacenti possono essere rappresentati distintamente. Al di sopra di esso, la spaziatura tra i valori rappresentabili aumenta, quindi alcuni interi decimali vicini vengono mappati allo stesso numero. Il runtime non tronca una stringa; sta selezionando il valore più vicino disponibile in quel formato binario finito.

Un controllo della console rivelatore è `Number.isSafeInteger(9007199254740993)`, che è falso, sebbene il valore letterale di origine sia già stato arrotondato prima che la funzione lo riceva. Un altro è `9007199254740992 === 9007199254740993`, che restituisce vero in JavaScript. Questi esempi riguardano l'identità intera esatta, non il fatto che ogni numero maggiore diventi inutilizzabile.

Il modo in cui l'analisi e la riserializzazione perde le cifre

La formattazione di analisi e riserializzazione prevede tre fasi: leggere caratteri numerici, creare un valore in memoria, quindi generare nuovi caratteri da quel valore. I dettagli lessicali scompaiono nella fase intermedia. Con `{"ticket":9223372036854775807}`, `JSON.parse` crea il numero JavaScript disponibile più vicino; `JSON.stringify` quindi emette `9223372036854776000`. Il serializzatore non danneggia in modo indipendente un token conservato. Al momento della serializzazione, la sequenza originale di cifre non è più presente nell'oggetto analizzato.

L'implementazione del repository di ToolAcre utilizza `JSON.parse` e `JSON.stringify`, quindi questa limitazione si applica al suo output formattato. Il suo scanner della sintassi viene eseguito per fornire una ragione e una posizione stabili dopo che l'analisi fallisce; non sostituisce i numeri JavaScript con una rappresentazione a precisione arbitraria. Un risultato di validazione positivo stabilisce quindi la grammatica, mentre una differenza di formattazione può rivelare una perdita di precisione.

Esempio realizzato: confrontare input e output

Confronta `{"numeric":9007199254740993,"text":"9007199254740993"}` prima e dopo un JavaScript viaggio di andata e ritorno. L'esecuzione di `JSON.stringify(JSON.parse(source), null, 2)` produce un oggetto formattato il cui membro `numeric` è `9007199254740992`, mentre `text` rimane `"9007199254740993"`. Entrambi i membri erano validi nell'input ed entrambi rimangono validi nell'output. Solo la rappresentazione tra virgolette preserva l'identificatore esattamente perché è decodificato come dati carattere anziché come numero.

Una recensione utile non si limita a chiedere se il formattatore è verde. Cerca nell'origine sequenze di cifre ininterrotte, confronta eventuali valori più lunghi dell'intervallo di sicurezza e determina se ciascun campo rappresenta una quantità o un'etichetta opaca. Se il produttore controlla il contratto, cambia l'etichetta in una stringa e documenta quella scelta per i consumatori.

Protezione degli ID alla fonte

Proteggi gli ID all'origine definendoli come stringhe nello schema e serializzandoli come stringhe prima che qualsiasi client JavaScript riceva il payload. Un ID può contenere solo cifre e avere comunque un significato non numerico: l'addizione, l'arrotondamento e l'ordinamento per grandezza non sono operazioni legittime su una chiave di account. Una stringa conserva anche gli zeri iniziali, che una rappresentazione numerica scarterebbe anche quando la sua grandezza rientra nell'intervallo di sicurezza.

Non dedurre la sicurezza tra linguaggi dal fatto che un altro runtime può contenere un numero intero più grande. I parser e i tipi di destinazione variano e un intermediario scritto in JavaScript può arrotondare il valore prima che un servizio successivo lo veda. Alcuni parser specializzati preservano i token numerici o costruiscono numeri interi di grandi dimensioni, ma ogni partecipante deve condividere quel contratto.

Ciò che questo non copre

Ciò che questo non copre è il disegno più ampio dell'aritmetica decimale. Valori come `0.1` hanno il proprio comportamento binario a virgola mobile e money può richiedere numeri interi scalati o tipi decimali in base al contratto dell'applicazione. Né citare ogni numero migliora automaticamente uno schema. I conteggi, le coordinate e le misurazioni sono spesso legittimamente numerici. La decisione dipende dal fatto se l'ortografia decimale esatta o l'identità intera esatta debbano sopravvivere a ogni consumatore nel percorso dati.

Inoltre, questa discussione non afferma che JSON abbia arrotondato il token o che tutti i parser si comportino come JavaScript. Le prove concrete del repository sono più ristrette: questo formattatore chiama `JSON.parse` e `JSON.stringify`, quindi JavaScript La semantica numerica governa qui i valori senza virgolette. Una libreria JSON a precisione arbitraria può effettuare scelte diverse, ma deve definire il modo in cui i valori vengono esposti e serializzati.

Da tenere in considerazione: i numeri sopra 2^53 appartengono alle stringhe

La conclusione è specifica: gli identificatori interi al di fuori dell'intervallo sicuro di JavaScript appartengono alle stringhe quando devono passare attraverso JavaScript senza cambiare. `9007199254740993` come numero JSON è una sintassi valida ma diventa `9007199254740992` dopo `JSON.parse`; `"9007199254740993"` rimane esatto. Le virgolette non sono decorazioni. Selezionano una rappresentazione che preservi le cifre come dati e impedisca ai consumatori di trattare un'etichetta opaca come una quantità approssimativa.

Prima di sostituire un documento con l'output del formattatore, confronta i numeri lunghi con l'originale ed esamina ogni cifra modificata. Correggi il produttore e lo schema quando possibile in modo che tutti i client downstream ricevano il modulo sicuro in modo coerente. ToolAcre può esporre la conseguenza perché il suo output riflette il valore JavaScript analizzato, ma non può ricostruire le cifre già perse durante l'analisi.