Outils de développement · JSON formateur et validateur
ID entiers volumineux dans JSON : pourquoi les formateurs JavaScript peuvent les arrondir
· Pourquoi c'est important
json flux de travail du développeur validation
JSON autorise les entiers de n'importe quelle taille, mais JavaScript représente les nombres sous forme de 64 bits flottants, donc tout ce qui dépasse 2^53 peut changer une fois analysé et re-sérialisé. Cet article explique la limite, comment repérer les dégâts et comment protéger les pièces d'identité. Historique des normes de
L'ID qui a changé de un
L'ID qui a changé par un arrive souvent comme parfaitement valide JSON. Mettez `{"orderId":9007199254740993}` dans JavaScript et `JSON.parse` renvoie un nombre dont la valeur affichée est `9007199254740992`. L'analyse réussit car le jeton suit la grammaire numérique JSON ; le dommage se produit lors de la conversion de ces chiffres décimaux en représentation numérique JavaScript. Un formateur qui sérialise la valeur analysée écrit fidèlement le nombre arrondi, et non le jeton exact qui apparaît dans la source.
Le contraste est immédiat lorsque les mêmes chiffres sont cités. `JSON.parse("{"orderId":"9007199254740993"}")` renvoie la chaîne `9007199254740993`, en préservant chaque caractère, et `JSON.stringify` émet ces chiffres inchangés entre guillemets. C'est pourquoi la validation syntaxique à elle seule ne peut pas protéger un identifiant numérique. Comparez l'entrée et la sortie chaque fois que des entiers longs apparaissent et traitez les identifiants comme des chaînes à la limite de production lorsque l'arithmétique ne fait pas partie de leur signification.
Ce que dit la RFC 8259 à propos des nombres
RFC 8259 définit l'orthographe d'un nombre JSON mais ne donne pas à chaque implémentation un type numérique de précision arbitraire. La grammaire autorise un signe moins facultatif, une partie entière et des parties facultatives de fraction et d’exposant. Il exclut les commodités telles que la notation hexadécimale, `NaN` et `Infinity`. Par conséquent, `9007199254740993` est syntaxiquement valide même si un consommateur JavaScript commun ne peut pas représenter cet entier exactement comme un nombre.
Les conseils d'interopérabilité de la spécification sont l'avertissement pratique : les logiciels utilisent couramment les nombres binaires 64 IEEE 754, et les entiers compris entre `2^53 + 1` négatif et `2^53 - 1` positif sont interopérables dans le sens d'un accord exact. Un validateur peut accepter correctement un jeton plus grand tandis qu'un analyseur l'arrondit ultérieurement.
D'où 2^53 vient
La limite `2^53` provient de la précision disponible dans une mantisse binaire64. JavaScript expose l'entier représentable consécutivement le plus élevé sous la forme `Number.MAX_SAFE_INTEGER`, qui est `9007199254740991`. À cette valeur et en dessous, les entiers adjacents peuvent être représentés distinctement. Au-dessus, l'espacement entre les valeurs représentables augmente, de sorte que certains entiers décimaux voisins correspondent au même nombre. Le runtime ne tronque pas une chaîne ; il sélectionne la valeur la plus proche disponible dans ce format binaire fini.
Une vérification de console révélatrice est `Number.isSafeInteger(9007199254740993)`, ce qui est faux, bien que le littéral source ait déjà été arrondi avant que la fonction ne le reçoive. Un autre est `9007199254740992 === 9007199254740993`, qui est évalué comme vrai en JavaScript. Ces exemples concernent l'identité exacte des entiers, et non la question de savoir si chaque nombre plus grand devient inutilisable.
Comment l'analyse et la resérialisation perdent des chiffres
Le formatage d'analyse et de resérialisation comporte trois étapes : lire les caractères numériques, créer une valeur en mémoire, puis générer de nouveaux caractères à partir de cette valeur. Les détails lexicaux disparaissent au stade intermédiaire. Avec `{"ticket":9223372036854775807}`, `JSON.parse` crée le numéro JavaScript disponible le plus proche ; `JSON.stringify` émet alors `9223372036854776000`. Le sérialiseur ne corrompt pas indépendamment un jeton préservé. Au moment de la sérialisation, la séquence originale de chiffres n'est plus présente dans l'objet analysé.
L'implémentation du référentiel de ToolAcre utilise `JSON.parse` et `JSON.stringify`, cette limitation s'applique donc à sa sortie formatée. Son scanner de syntaxe s'exécute pour fournir une raison et un emplacement stables après l'échec de l'analyse ; il ne remplace pas les nombres JavaScript par une représentation de précision arbitraire. Un résultat de validation réussi établit donc la grammaire, tandis qu'une différence de formatage peut révéler une perte de précision.
Exemple concret : comparaison des entrées et des sorties
Comparez `{"numeric":9007199254740993,"text":"9007199254740993"}` avant et après un aller-retour JavaScript. L'exécution de `JSON.stringify(JSON.parse(source), null, 2)` produit un objet formaté dont le membre `numeric` est `9007199254740992`, tandis que `text` reste `"9007199254740993"`. Les deux membres étaient valides dans l’entrée et les deux restent valides dans la sortie. Seule la représentation citée préserve exactement l'identifiant car il est décodé sous forme de données de caractères plutôt que sous forme de nombre.
Un examen utile ne consiste pas simplement à demander si le formateur est affiché en vert. Recherchez dans la source des séquences de chiffres ininterrompues, comparez les valeurs plus longues que la plage de sécurité et déterminez si chaque champ représente une quantité ou une étiquette opaque. Si le producteur contrôle le contrat, remplacez l'étiquette par une chaîne et documentez ce choix pour les consommateurs.
Protection des identifiants à la source
Protégez les ID à la source en les définissant sous forme de chaînes dans le schéma et en les sérialisant sous forme de chaînes avant qu'un client JavaScript ne reçoive la charge utile. Un identifiant peut contenir uniquement des chiffres tout en ayant une signification non numérique : l'addition, l'arrondi et le classement par grandeur ne sont pas des opérations légitimes sur une clé de compte. Une chaîne conserve également les zéros non significatifs, qu'une représentation numérique ignorerait même lorsque son ampleur se situe dans la plage de sécurité.
Ne déduisez pas la sécurité multilingue du fait qu'un autre environnement d'exécution peut contenir un entier plus grand. Les analyseurs et les types de cibles varient, et un intermédiaire écrit en JavaScript peut arrondir la valeur avant qu'un service ultérieur ne la voie. Certains analyseurs spécialisés conservent les jetons numériques ou construisent de grands entiers, mais chaque participant doit partager ce contrat.
Ce que cela ne couvre pas
Ce que cela ne couvre pas, c'est la conception plus large de l'arithmétique décimale. Les valeurs telles que `0.1` ont leur propre comportement binaire à virgule flottante, et l'argent peut nécessiter des entiers mis à l'échelle ou des types décimaux selon le contrat d'application. Le fait de citer chaque nombre n’améliore pas non plus automatiquement un schéma. Les décomptes, les coordonnées et les mesures sont souvent légitimement numériques. La décision dépend de la question de savoir si l'orthographe décimale exacte ou l'identité entière exacte doit survivre à chaque consommateur dans le chemin de données.
Cette discussion ne prétend pas non plus que JSON lui-même a arrondi le jeton ou que tous les analyseurs se comportent comme JavaScript. La preuve concrète du référentiel est plus restreinte : ce formateur appelle `JSON.parse` et `JSON.stringify`, donc la sémantique des nombres JavaScript régit ici les valeurs non citées. Une bibliothèque JSON de précision arbitraire peut faire différents choix, mais elle doit définir la manière dont les valeurs sont exposées et sérialisées.
À retenir : les nombres au-dessus de 2^53 appartiennent à des chaînes
Ce qu'il faut retenir est spécifique : les identifiants entiers en dehors de la plage de sécurité de JavaScript appartiennent à des chaînes lorsqu'ils doivent passer par JavaScript sans changer. `9007199254740993` en tant que nombre JSON est une syntaxe valide mais devient `9007199254740992` après `JSON.parse` ; `"9007199254740993"` reste exact. Les citations ne sont pas une décoration. Ils sélectionnent une représentation qui préserve les chiffres en tant que données et empêche les consommateurs de traiter une étiquette opaque comme une quantité approximative.
Avant de remplacer un document par la sortie du formateur, comparez les nombres longs avec l'original et examinez chaque chiffre modifié. Corrigez le producteur et le schéma lorsque cela est possible afin que tous les clients en aval reçoivent le formulaire sécurisé de manière cohérente. ToolAcre peut exposer la conséquence car sa sortie reflète la valeur JavaScript analysée, mais il ne peut pas reconstruire les chiffres déjà perdus lors de l'analyse.