Español

Herramientas de desarrollo · JSON formateador y validador

ID de números enteros grandes en JSON: por qué los formateadores de JavaScript pueden redondearlos

· Por qué es importante

json flujo de trabajo del desarrollador validación

ID de números enteros grandes en JSON: por qué los formateadores de JavaScript pueden redondearlos ilustrados con tokens JSON y un límite de validación preciso
Ilustración de vector original de ToolAcre

JSON permite números enteros de cualquier tamaño, pero JavaScript representa números como 64 bits flotantes, por lo que cualquier valor superior a 2^53 puede cambiar cuando se analiza y se vuelve a serializar. Esta publicación explica el límite, cómo detectar el daño y cómo proteger las identificaciones. Historial de estándares de

El ID que cambió por uno

El ID que se cambió por uno a menudo llega como JSON perfectamente válido. Coloque `{"orderId":9007199254740993}` en JavaScript y `JSON.parse` devuelve un número cuyo valor mostrado es `9007199254740992`. El análisis se realiza correctamente porque el token sigue la gramática numérica JSON; el daño ocurre al convertir esos dígitos decimales en la representación numérica de JavaScript. Un formateador que serializa el valor analizado escribe fielmente el número redondeado, no el token exacto que apareció en la fuente.

El contraste es inmediato cuando se citan los mismos dígitos. `JSON.parse("{"orderId":"9007199254740993"}")` devuelve la cadena `9007199254740993`, conservando cada carácter, y `JSON.stringify` emite esos dígitos sin cambios entre comillas. Esta es la razón por la que la validación de sintaxis por sí sola no puede proteger un identificador numérico. Compare la entrada y la salida siempre que aparezcan números enteros largos y trate los identificadores como cadenas en el límite de producción cuando la aritmética no sea parte de su significado.

Lo que dice el RFC 8259 sobre los números

RFC 8259 define la ortografía de un número JSON pero no proporciona a cada implementación un tipo numérico de precisión arbitraria. La gramática permite un signo menos opcional, una porción entera y porciones opcionales de fracción y exponente. Excluye comodidades como la notación hexadecimal, `NaN` y `Infinity`. En consecuencia, `9007199254740993` es sintácticamente válido aunque un consumidor común de JavaScript no pueda representar ese número entero exactamente como un Número.

La guía de interoperabilidad de la especificación es la advertencia práctica: el software comúnmente usa números binarios 64 IEEE 754, y los números enteros en el rango desde `2^53 + 1` negativo hasta `2^53 - 1` positivo son interoperables en el sentido de concordancia exacta. Un validador puede aceptar correctamente un token más grande mientras que un analizador lo redondea posteriormente.

De dónde viene 2^53

El límite `2^53` proviene de la precisión disponible en un significado binario64. JavaScript expone el entero más alto representable consecutivamente como `Number.MAX_SAFE_INTEGER`, que es `9007199254740991`. En esa magnitud y por debajo de ella, los números enteros adyacentes se pueden representar claramente. Por encima de él, el espacio entre valores representables crece, por lo que algunos enteros decimales vecinos se asignan al mismo número. El tiempo de ejecución no trunca una cadena; está seleccionando el valor más cercano disponible en ese formato binario finito.

Una comprobación reveladora de la consola es `Number.isSafeInteger(9007199254740993)`, que es falsa, aunque el literal fuente ya se ha redondeado antes de que la función lo reciba. Otro es `9007199254740992 === 9007199254740993`, que se evalúa como verdadero en JavaScript. Estos ejemplos se refieren a la identidad entera exacta, no a si cada número mayor se vuelve inutilizable.

Cómo el análisis y la reserialización pierden dígitos

El formato de análisis y reserialización tiene tres etapas: leer caracteres numéricos, crear un valor en memoria y luego generar caracteres nuevos a partir de ese valor. Los detalles léxicos desaparecen en la etapa intermedia. Con `{"ticket":9223372036854775807}`, `JSON.parse` crea el número JavaScript disponible más cercano; `JSON.stringify` luego emite `9223372036854776000`. El serializador no corrompe de forma independiente un token conservado. En el momento de la serialización, la secuencia original de dígitos ya no está presente en el objeto analizado.

La implementación del repositorio de ToolAcre utiliza `JSON.parse` y `JSON.stringify`, por lo que esta limitación se aplica a su salida formateada. Su escáner de sintaxis se ejecuta para proporcionar un motivo y una ubicación estables después de que falla el análisis; no reemplaza los números de JavaScript con una representación de precisión arbitraria. Por lo tanto, un resultado de validación exitoso establece la gramática, mientras que una diferencia de formato puede revelar una pérdida de precisión.

Ejemplo resuelto: comparar entrada y salida

Compara `{"numeric":9007199254740993,"text":"9007199254740993"}` antes y después de un viaje de ida y vuelta de JavaScript. La ejecución de `JSON.stringify(JSON.parse(source), null, 2)` produce un objeto formateado cuyo miembro `numeric` es `9007199254740992`, mientras que `text` sigue siendo `"9007199254740993"`. Ambos miembros eran válidos en la entrada y ambos siguen siendo válidos en la salida. Sólo la representación entre comillas conserva el identificador exactamente porque se decodifica como datos de caracteres en lugar de un número.

Una revisión útil no se limita a preguntar si el formateador se mostró en verde. Busque en la fuente secuencias de dígitos ininterrumpidas, compare cualquier valor que supere el rango seguro y determine si cada campo representa una cantidad o una etiqueta opaca. Si el productor controla el contrato, cambie la etiqueta a una cadena allí y documente esa elección para los consumidores.

Protección de identificaciones en la fuente

Proteja los ID en el origen definiéndolos como cadenas en el esquema y serializándolos como cadenas antes de que cualquier cliente JavaScript reciba la carga útil. Una identificación puede contener solo dígitos y aun así tener un significado no numérico: sumar, redondear y ordenar por magnitud no son operaciones legítimas en una clave de cuenta. Una cadena también conserva los ceros iniciales, que una representación numérica descartaría incluso cuando su magnitud esté dentro del rango seguro.

No infieras la seguridad entre lenguajes del hecho de que otro tiempo de ejecución pueda contener un número entero mayor. Los analizadores y los tipos de destino varían, y un intermediario escrito en JavaScript puede redondear el valor antes de que lo vea un servicio posterior. Algunos analizadores especializados conservan tokens numéricos o construyen números enteros grandes, pero cada participante debe compartir ese contrato.

Lo que esto no cubre

Lo que esto no cubre es el diseño más amplio de la aritmética decimal. Valores como `0.1` tienen su propio comportamiento binario de punto flotante y el dinero puede requerir números enteros escalados o tipos decimales según el contrato de aplicación. Citar todos los números tampoco mejora automáticamente un esquema. Los recuentos, coordenadas y medidas suelen ser legítimamente numéricos. La decisión depende de si la ortografía decimal exacta o la identidad entera exacta deben sobrevivir a cada consumidor en la ruta de datos.

Esta discusión tampoco afirma que JSON haya redondeado el token o que todos los analizadores se comporten como JavaScript. La evidencia concreta del repositorio es más limitada: este formateador llama a `JSON.parse` y `JSON.stringify`, por lo que la semántica de números de JavaScript rige los valores sin comillas aquí. Una biblioteca JSON de precisión arbitraria puede tomar diferentes decisiones, pero debe definir cómo se exponen y serializan los valores.

Conclusión: los números anteriores a 2^53 pertenecen a cadenas

La conclusión es específica: los identificadores enteros fuera del rango seguro de JavaScript pertenecen a cadenas cuando deben pasar a través de JavaScript sin cambios. `9007199254740993` como número JSON es una sintaxis válida pero se convierte en `9007199254740992` después de `JSON.parse`; `"9007199254740993"` sigue siendo exacto. Las citas no son decoración. Seleccionan una representación que preserva los dígitos como datos y evita que los consumidores traten una etiqueta opaca como una cantidad aproximada.

Antes de reemplazar un documento con salida del formateador, compare números largos con el original e investigue cada dígito modificado. Corrija el productor y el esquema cuando sea posible para que todos los clientes posteriores reciban el formulario seguro de manera consistente. ToolAcre puede exponer las consecuencias porque su salida refleja el valor de JavaScript analizado, pero no puede reconstruir los dígitos que ya se perdieron durante el análisis.