O que cada formato realmente garante
Antes de converter, vale saber o contrato de cada formato, não a impressão, mas o que a especificação promete. JSON é definido pela RFC 8259 e pela ECMA-404: uma sintaxe minúscula, sem comentários, sem vírgula sobrando, e cujo texto, quando trocado entre sistemas abertos, deve ser codificado em UTF-8. YAML, na spec 1.2.2, é declaradamente um superconjunto do JSON, com foco em legibilidade humana. TOML, na versão 1.0.0 (estável desde janeiro de 2021), nasceu para configuração óbvia e sem ambiguidade. Os três descrevem os mesmos dados; divergem no que assumem por você.
JSON (RFC 8259)
- Rígido: aspas duplas obrigatórias, nada de comentário ou vírgula final.
- Sem tipo data e sem inteiro de precisão infinita definidos na spec.
- Ideal para troca máquina-a-máquina; ruidoso para editar à mão.
YAML (1.2.2)
- Superconjunto do JSON, com indentação, comentários (#) e âncoras.
- Muitos escalares implícitos: onde nasce o "problema da Noruega".
- Limpo para humanos, sensível a um espaço a mais.
TOML (1.0.0)
- Tipos explícitos: string, inteiro, data-hora e tabela sem adivinhação.
- Sem indentação significativa; seções entre colchetes.
- Feito para arquivo de configuração de projeto (Cargo, pyproject).
O conversor de dados trabalha com JSON, YAML, CSV e XML (TOML fica de fora, mas serve de referência quando você quer tipos sem surpresa). CSV e XML acrescentam sua própria armadilha: CSV não tem noção de tipo, toda célula é texto,, e XML mistura elementos com atributos, sem um mapa único para objeto. A tabela abaixo resume o que cada formato do conversor entrega.
| Formato | Tem tipos? | Comentários? | Especificação | Uso típico |
|---|---|---|---|---|
| JSON | Sim (limitado) | Não | RFC 8259 / ECMA-404 | APIs, troca de dados |
| YAML | Sim (implícito) | Sim (#) | YAML 1.2.2 | Configuração |
| CSV | Não (tudo texto) | Não | RFC 4180 (de facto) | Planilhas, tabelas |
| XML | Não (texto + atributos) | Sim (marcador) | XML 1.0 (W3C) | Integrações, documentos |
JSON e YAML: superconjunto, não sinônimos
A spec YAML 1.2.2 diz na introdução que o foco principal da versão 1.2 foi "tornar o YAML um superconjunto estrito do JSON" e que ela "removeu muitas das recomendações problemáticas de tipagem implícita". Traduzindo: todo JSON válido é YAML válido, mas nem todo YAML é JSON, e a diferença de estilo é grande. JSON marca estrutura com chaves, colchetes e aspas, feio para o olho, impossível de indentar errado. YAML usa indentação por espaços (tab é erro de sintaxe) e permite omitir aspas, o que fica limpo em configuração ao custo de escalares ambíguos. O mesmo dado nos dois:
// JSON, estrutura explícita / explicit structure
{ "app": "jkit", "port": 3000, "tags": ["web", "tools"] }
# YAML equivalente / equivalent YAML
app: jkit # comentário permitido / comment allowed
port: 3000
tags:
- web
- toolsA palavra que importa na frase da spec é "estrito": a compatibilidade vale para a sintaxe, não para a interpretação de cada valor solto. E é justamente na tipagem implícita, o que um "NO" ou um "1.10" sem aspas viram, que as versões do YAML divergem. Por isso a data da spec importa tanto quanto o número dela. Veja como os padrões chegaram até aqui:
- 2001JSON é especificado
Douglas Crockford descreve o formato em json.org, extraído da sintaxe de objeto do JavaScript.
- 2005YAML 1.1
O schema com tipagem implícita larga: "yes", "no", "on", "off" e "NO" resolvem para booleano. É a origem do problema da Noruega.
- 2006RFC 4627
A primeira RFC do JSON registra o formato como padrão de internet.
- 2009YAML 1.2
Vira superconjunto estrito do JSON e introduz o core schema, que restringe o booleano a true/false.
- 2013ECMA-404 e TOML
A sintaxe do JSON é padronizada como ECMA-404; no mesmo ano nasce o TOML, focado em configuração.
- 2017 e 2021RFC 8259, YAML 1.2.2, TOML 1.0.0
A RFC 8259 (STD 90) consolida o JSON; em 2021 saem a revisão YAML 1.2.2 e a primeira versão estável do TOML.
As três armadilhas que corrompem em silêncio
A maioria dos bugs de conversão não é erro de sintaxe visível, é uma reinterpretação silenciosa de tipo. O documento converte sem reclamar; o dado é que mudou. Três armadilhas causam a maior parte da dor, e as três dependem de qual parser e qual schema você usou.
Armadilha 1, o problema da Noruega. No YAML 1.1, os escalares "NO", "no", "on", "off", "yes", "y" e "n" sem aspas são booleanos. O código do país Noruega é NO; sem aspas, ele resolve para false. O PyYAML segue o schema 1.1 por padrão, então este é o comportamento real de milhões de arquivos. Passe o YAML abaixo por um parser 1.1 e veja o país sumir:
# Entrada YAML (interpretada com o schema 1.1, ex.: PyYAML)
# YAML input (interpreted with the 1.1 schema, e.g. PyYAML)
country: NO
shipping:
express: yes
weekend: off
// Saída JSON, os escalares viram booleanos
// JSON output, the scalars become booleans
{
"country": false,
"shipping": { "express": true, "weekend": false }
}O YAML 1.2 corrigiu isso: o core schema resolve como booleano apenas true, True, TRUE, false, False e FALSE. Confirme sempre qual schema o seu parser usa. O conversor de dados desta página, por exemplo, roda sobre a biblioteca js-yaml, que na versão instalada usa o core schema 1.2 por padrão, logo, "country: NO" chega como a string "NO", e não como false. A defesa universal, independente do parser, é uma só: coloque aspas em qualquer código, sigla ou valor que possa colidir com um booleano.
Armadilha 2, precisão de inteiros. A spec do JSON não impõe limite ao tamanho de um número, mas JavaScript representa todo número como um ponto flutuante de 64 bits (IEEE 754 binary64). O maior inteiro que cabe sem perda é o Number.MAX_SAFE_INTEGER, definido na especificação ECMAScript. Acima dele, os inteiros pulam de dois em dois, de quatro em quatro, e a conta silenciosamente arredonda.
Number.MAX_SAFE_INTEGER = 2^53 - 1 = 9007199254740991- 2^53
- a mantissa de um float64 tem 52 bits explícitos + 1 implícito, o que dá 53 bits de precisão inteira
- - 1
- o maior inteiro representável sem saltos é 2^53 menos um
// Entrada JSON, dois ids acima do limite seguro
// JSON input, two ids above the safe limit
{ "orderId": 9007199254740993, "userId": 1541815603606036480 }
// Depois de JSON.parse e serializar de novo (JavaScript)
// After JSON.parse and re-serializing (JavaScript)
{ "orderId": 9007199254740992, "userId": 1541815603606036500 }Armadilha 3, datas. O JSON não tem tipo data: uma data é sempre uma string, e cabe a você combinar o formato (ISO 8601 é o de fato). O YAML 1.1, ao contrário, tem timestamp implícito, um "2026-05-05" sem aspas é convertido para um objeto de data pelo parser, o que muda o tipo do valor sem você pedir. Ao ir de YAML para JSON, esse objeto vira uma string de data que pode não bater com o formato que o outro lado espera. Se o seu campo é uma versão ("2026.05"), um código ou uma data que precisa de fuso, coloque aspas, e, quando o fuso entra na conta, o guia de fusos horários mostra por que UTC e horário de verão transformam uma string aparentemente inofensiva em bug.
Chaves duplicadas, âncoras e a bomba YAML
Além das três armadilhas de tipo, há um segundo grupo de riscos que vive na estrutura e na segurança do parser. Chaves repetidas, âncoras que se multiplicam e um carregador inseguro podem, respectivamente, perder dados, derrubar o servidor e executar código. Cada um dobra para fora do fluxo principal abaixo, abra o que interessa.
Chaves duplicadas: permitidas na sintaxe, indefinidas no efeito
A RFC 8259 (§4) diz que "os nomes dentro de um objeto DEVERIAM ser únicos" e, se não forem, "o comportamento do software que recebe esse objeto é imprevisível". Algumas bibliotecas ficam com o último par, outras com o primeiro, outras acumulam todos, outras dão erro. Ou seja: um JSON com a mesma chave duas vezes é sintaticamente válido, mas o valor que sobrevive depende do parser.
O YAML é mais rígido: a spec proíbe chaves de mapeamento duplicadas. Ainda assim, nem todo parser reclama, vários simplesmente ficam com a última. A defesa é validar unicidade antes de converter, especialmente quando dois arquivos de configuração são mesclados e uma chave aparece nos dois.
Âncoras e aliases: reúso com & e *
O YAML deixa você nomear um nó com uma âncora (&nome) e reusá-lo depois com um alias (*nome), evitando repetição em arquivos grandes. É prático, mas tem duas consequências: ao converter para JSON, os aliases são expandidos, a estrutura reusada é copiada em cada ponto, e o arquivo pode inchar. E, como qualquer valor apontado por alias, a expansão acontece na hora do parse, não na leitura.
Billion laughs: a bomba YAML
O reúso por alias vira arma quando é aninhado. Você define uma âncora com uma lista de dez itens, define uma segunda âncora com dez cópias da primeira, uma terceira com dez cópias da segunda, e assim por diante. Cada nível multiplica o tamanho por dez; poucas linhas de texto se expandem para bilhões de nós na memória. É o ataque billion laughs (ou YAML bomb), um negação de serviço: o parser tenta materializar a estrutura e o processo é morto por falta de memória.
A mitigação é limitar a expansão: os parsers modernos contam as resoluções de alias e param num teto (o pacote yaml do npm limita a 100 por padrão; o SnakeYAML, a 128). Ao aceitar YAML de fonte não confiável, use um parser com esse limite ou rejeite aliases de vez.
Por que o JSON não tem comentários
A gramática da RFC 8259 não prevê comentário: os únicos espaços em branco insignificantes são espaço, tab, quebra de linha e retorno de carro. Um // ou /* */ invalida o documento inteiro. Foi uma escolha deliberada de Douglas Crockford, comentários eram removidos por alguns geradores para embutir diretivas de parsing, o que quebrava a interoperabilidade. Se você precisa de anotação em um JSON, use um campo de dado (por exemplo "_comment"); se precisa de comentário de verdade, esse é um bom sinal de que o formato certo é YAML ou TOML.
Convertendo na prática, sem corromper
Veja uma conversão real de CSV para JSON. A primeira linha do CSV vira as chaves; cada linha seguinte vira um objeto. Como CSV não guarda tipo, todo valor chega como string, inclusive números e booleanos, e um campo vazio vira "". É o caso mais óbvio de perda de tipo, e o que mais surpreende quem espera que o 42 continue número:
name,age,active
Pedro,42,true
Ana,,false
// vira / becomes:
[
{ "name": "Pedro", "age": "42", "active": "true" },
{ "name": "Ana", "age": "", "active": "false" }
]No sentido inverso, JSON para CSV, estruturas aninhadas como uma lista de papéis são serializadas como texto dentro da célula, porque uma tabela plana não representa hierarquia. Junte isso ao que já vimos e o mapa de risco fica assim:
| Armadilha | Quem sofre | Como evitar |
|---|---|---|
| Booleano acidental (Noruega) | Parsers com schema YAML 1.1 (PyYAML) | Aspas no valor; use um parser 1.2 |
| Perda de precisão de inteiro | JSON.parse e qualquer runtime JavaScript | Trate ids grandes como string |
| Data convertida sozinha | Timestamp implícito do YAML 1.1 | Aspas; combine ISO 8601 |
| Tipo apagado | CSV (tudo vira texto) | Coaja o tipo após converter |
| Chave duplicada | JSON (indefinido) e parsers YAML frouxos | Valide unicidade antes de mesclar |
| Execução de código / bomba | yaml.load inseguro; parser sem limite de alias | safe_load e limite de expansão |
A melhor forma de internalizar tudo isso é experimentar. Cole um YAML com um "NO" sem aspas, um id gigante ou uma data solta no conversor abaixo e veja o que sai do outro lado, tudo no navegador, sem enviar nada para um servidor:
Localize, converta e valide o tipo
Muitas vezes você não quer o documento inteiro, só um valor lá no fundo. Um caminho (path) é o endereço desse valor: partindo da raiz $, você desce por chaves e índices, $.user.roles[0] pega o primeiro papel do usuário. O localizador de caminhos JSON devolve o caminho de qualquer nó que você clicar, o que ajuda a inspecionar exatamente o campo que vai converter antes de mexer no arquivo todo. Extrair valores por padrão de texto é a outra metade do trabalho, coberta pelo guia de regex do zero.
- Confirme qual schema o seu parser YAML usa (1.1 vs 1.2) antes de confiar em qualquer escalar sem aspas.
- Coloque aspas em códigos, siglas, versões e datas que possam colidir com booleano, número ou timestamp.
- Trate ids e valores monetários acima de 2^53 − 1 como string, do banco ao JSON e de volta.
- Valide a unicidade das chaves ao mesclar arquivos de configuração.
- Use safe_load (ou equivalente) para YAML de fonte não confiável, com limite de expansão de alias.
- Depois de estruturar os dados, formate o resto do pipeline, deixe a query legível no formatador de SQL.
O padrão que resolve quase tudo é o mesmo em qualquer conversão: localize o valor com um caminho, converta com um parser cujo schema você conhece e valide o tipo do outro lado. A conversão nunca é o problema; a suposição de que o tipo sobreviveu intacto é. Se os seus ids seguem outro esquema além de inteiros, o guia de UUID e ULID mostra alternativas que não passam nem perto do limite do float64.
Perguntas frequentes
Por que "NO" vira false quando converto YAML?
Por que meu id grande mudou de valor ao converter?
YAML aceita comentário e JSON não?
Chave duplicada em JSON é erro?
yaml.load do PyYAML é seguro?
Qual formato usar para configuração: JSON, YAML ou TOML?
JSON e YAML descrevem os mesmos dados com filosofias diferentes, e a conversão é segura só quando você respeita o que cada um assume. As três armadilhas silenciosas são o problema da Noruega (um "NO" que vira false no schema YAML 1.1), a perda de precisão de inteiros acima de 2^53 − 1 = 9.007.199.254.740.991 e as datas que o YAML 1.1 converte sozinho. Some a isso chaves duplicadas, âncoras que explodem em billion laughs e o yaml.load inseguro. A defesa cabe numa frase: use aspas nos escalares ambíguos, trate ids grandes como string, confira o schema do parser e valide o tipo dos dois lados.
Fontes e referências
- RFC 8259, The JavaScript Object Notation (JSON) Data Interchange Format
- ECMA-404, The JSON Data Interchange Syntax
- YAML 1.2.2, Especificação oficial (inclui o core schema, §10.3)
- ECMAScript (ECMA-262), Number.MAX_SAFE_INTEGER
- PyYAML, orientação oficial sobre yaml.load e safe_load
- TOML v1.0.0, Especificação
- js-yaml, parser YAML usado pelo conversor (schema padrão)