Português (Brasil)

Ferramentas para desenvolvedores · Formatador e validador JSON

IDs inteiros grandes em JSON: por que os formatadores JavaScript podem arredondá-los

· Por que é importante

JSON fluxo de trabalho do desenvolvedor validação

IDs inteiros grandes em JSON: por que os formatadores JavaScript podem arredondá-los ilustrados com tokens JSON e um limite de validação preciso
Ilustração vetorial original ToolAcre

JSON permite números inteiros de qualquer tamanho, mas JavaScript representa números como 64 bits flutuantes, portanto, qualquer coisa acima de 2^53 pode mudar quando analisado e serializado novamente. Esta postagem explica o limite, como detectar os danos e como proteger as identidades.

O ID que mudou em um

O ID alterado por um geralmente chega como JSON perfeitamente válido. Coloque `{"orderId":9007199254740993}` em JavaScript e `JSON.parse` retorna um número cujo valor exibido é `9007199254740992`. A análise foi bem-sucedida porque o token segue a gramática numérica JSON; o dano ocorre ao converter esses dígitos decimais na representação numérica de JavaScript. Um formatador que serializa o valor analisado grava fielmente o número arredondado, não o token exato que apareceu na fonte.

O contraste é imediato quando os mesmos dígitos são citados. `JSON.parse("{"orderId":"9007199254740993"}")` retorna a string `9007199254740993`, preservando todos os caracteres, e `JSON.stringify` emite esses dígitos inalterados entre aspas. É por isso que a validação de sintaxe por si só não pode proteger um identificador numérico. Compare a entrada e a saída sempre que inteiros longos aparecerem e trate os identificadores como strings no limite de produção quando a aritmética não fizer parte de seu significado.

O que RFC 8259 diz sobre números

RFC 8259 define a ortografia de um número JSON, mas não fornece a cada implementação um tipo numérico de precisão arbitrária. A gramática permite um sinal de menos opcional, uma parte inteira e frações opcionais e porções de expoentes. Exclui conveniências como notação hexadecimal, `NaN` e `Infinity`. Conseqüentemente, `9007199254740993` é sintaticamente válido mesmo que um consumidor JavaScript comum não possa representar esse número inteiro exatamente como um número.

A orientação de interoperabilidade da especificação é o aviso prático: o software geralmente usa números binários IEEE 754 binário64, e números inteiros na faixa de `2^53 + 1` negativo a `2^53 - 1` positivo são interoperáveis ​​no sentido de concordância exata. Um validador pode aceitar corretamente um token maior enquanto um analisador o arredonda posteriormente.

De onde vem 2^53

O limite `2^53` vem da precisão disponível em um significando binário64. JavaScript expõe o número inteiro representável consecutivamente mais alto como `Number.MAX_SAFE_INTEGER`, que é `9007199254740991`. Nessa magnitude e abaixo dela, inteiros adjacentes podem ser representados distintamente. Acima dele, o espaçamento entre os valores representáveis ​​aumenta, de modo que alguns inteiros decimais vizinhos são mapeados para o mesmo número. O tempo de execução não está truncando uma string; está selecionando o valor mais próximo disponível nesse formato binário finito.

Uma verificação reveladora do console é `Number.isSafeInteger(9007199254740993)`, que é falsa, embora o literal de origem já tenha sido arredondado antes de a função recebê-lo. Outro é `9007199254740992 === 9007199254740993`, que é avaliado como verdadeiro em JavaScript. Esses exemplos dizem respeito à identidade inteira exata, e não se todo número maior se torna inutilizável.

Como analisar e reserializar perde dígitos

A formatação de análise e reserialização tem três estágios: ler caracteres numéricos, criar um valor na memória e, em seguida, gerar novos caracteres a partir desse valor. Os detalhes lexicais desaparecem no estágio intermediário. Com `{"ticket":9223372036854775807}`, `JSON.parse` cria o número JavaScript disponível mais próximo; `JSON.stringify` então emite `9223372036854776000`. O serializador não está corrompendo de forma independente um token preservado. No momento da serialização, a sequência original de dígitos não está mais presente no objeto analisado.

A implementação do repositório de ToolAcre usa `JSON.parse` e `JSON.stringify`, portanto, esta limitação se aplica à sua saída formatada. Seu scanner de sintaxe é executado para fornecer um motivo e localização estáveis ​​após falha na análise; ele não substitui números JavaScript por uma representação de precisão arbitrária. Um resultado de validação bem-sucedido, portanto, estabelece a gramática, enquanto uma comparação de formatação pode revelar perda de precisão.

Exemplo resolvido: comparando entrada e saída

Compare `{"numeric":9007199254740993,"text":"9007199254740993"}` antes e depois de uma viagem de ida e volta JavaScript. A execução de `JSON.stringify(JSON.parse(source), null, 2)` produz um objeto formatado cujo membro `numeric` é `9007199254740992`, enquanto `text` permanece `"9007199254740993"`. Ambos os membros eram válidos na entrada e ambos permanecem válidos na saída. Somente a representação entre aspas preserva o identificador exatamente porque ele é decodificado como dados de caracteres em vez de um número.

Uma revisão útil não apenas pergunta se o formatador ficou verde. Pesquise na fonte sequências de dígitos ininterruptas, compare quaisquer valores maiores que o intervalo seguro e determine se cada campo representa uma quantidade ou um rótulo opaco. Se o produtor controlar o contrato, altere o rótulo para uma string e documente essa escolha para os consumidores.

Protegendo IDs na origem

Proteja IDs na origem definindo-os como strings no esquema e serializando-os como strings antes que qualquer cliente JavaScript receba a carga útil. Um ID pode conter apenas dígitos e ainda assim ter um significado não numérico: adição, arredondamento e ordenação por magnitude não são operações legítimas em uma chave de conta. Uma string também preserva zeros à esquerda, que uma representação numérica descartaria mesmo quando sua magnitude estivesse dentro da faixa segura.

Não infira segurança entre linguagens pelo fato de que outro tempo de execução pode conter um número inteiro maior. Os analisadores e os tipos de destino variam, e um intermediário escrito em JavaScript pode arredondar o valor antes que um serviço posterior o veja. Alguns analisadores especializados preservam tokens numéricos ou constroem números inteiros grandes, mas cada participante deve comcomcompartilhar esse contrato.

O que isso não cobre

O que isto não cobre é o design mais amplo da aritmética decimal. Valores como `0.1` têm seu próprio comportamento de ponto flutuante binário e o dinheiro pode exigir números inteiros escalonados ou tipos decimais de acordo com o contrato do aplicativo. Nem citar todos os números melhora automaticamente um esquema. Contagens, coordenadas e medições são muitas vezes legitimamente numéricas. A decisão depende se a ortografia decimal exata ou a identidade inteira exata deve sobreviver a cada consumidor no caminho de dados.

Esta discussão também não afirma que o próprio JSON arredondou o token ou que todos os analisadores se comportam como JavaScript. A evidência concreta do repositório é mais restrita: este formatador chama `JSON.parse` e `JSON.stringify`, então JavaScript A semântica numérica governa os valores não citados aqui. Uma biblioteca JSON de precisão arbitrária pode fazer escolhas diferentes, mas deve definir como os valores são expostos e serializados.

Conclusão: os números acima de 2^53 pertencem a strings

A conclusão é específica: identificadores inteiros fora do intervalo seguro de JavaScript pertencem a strings quando devem passar por JavaScript sem serem alterados. `9007199254740993` como um número JSON é uma sintaxe válida, mas se torna `9007199254740992` após `JSON.parse`; `"9007199254740993"` permanece exato. As citações não são decoração. Eles selecionam uma representação que preserva os dígitos como dados e evita que os consumidores tratem um rótulo opaco como uma quantidade aproximada.

Antes de substituir um documento pela saída do formatador, compare números longos com o original e investigue cada dígito alterado. Corrija o produtor e o esquema quando possível para que todos os clientes downstream recebam o formulário seguro de forma consistente. ToolAcre pode expor a consequência porque sua saída reflete o valor JavaScript analisado, mas não pode reconstruir dígitos já perdidos durante a análise.