Conversor de JSON para YAML

Converte JSON em YAML e explica cada aspa que acrescenta: as cadeias que sem ela se tornariam booleanos, números ou datas em silêncio.

Entrada
YAML
name: deploy
country: 'NO'
startsAt: '12:30'
mode: '0755'
version: '1.10'
released: '2024-01-30'
enabled: 'yes'
script: |-
  set -e
  npm run build
  npm test
replicas: 3
tags:
  - web
  - edge
Porque leva aspas cada cadeia

Valores entre aspas: 6. Necessárias só em YAML 1.1: 4. Mude o esquema acima para ver a diferença.

  • countrysó 1.1

    «NO» seria lido como o booleano false.

  • startsAtsó 1.1

    «12:30» seria lido na base 60 como 750.

  • mode

    «0755» tem um zero inicial, por isso seria lido como o número 493.

  • version

    «1.10» seria lido como o número 1.1.

  • releasedsó 1.1

    «2024-01-30» seria lido como uma data e não como texto.

  • enabledsó 1.1

    «yes» seria lido como o booleano true.

O que esta ferramenta faz

Converte um documento JSON em YAML e depois diz-lhe porque cada cadeia acabou entre aspas. Essa segunda parte é a razão de a ferramenta existir: qualquer outro conversor devolve a saída e deixa-o descobrir mais tarde que um dos seus valores já não é uma cadeia.

A conversão vai num só sentido. Ler YAML é um problema muito maior do que escrevê-lo, e um analisador de YAML parcialmente correto é pior do que nenhum: aceita o seu ficheiro e devolve dados errados sem se queixar. Emitir é um problema delimitado, e é esse o sentido coberto aqui.

Porque é que um conversor precisa de opiniões

O JSON diz de que tipo é cada coisa. Uma cadeia tem aspas, um número não, e não há uma terceira possibilidade. O YAML, em vez disso, decide o tipo de um valor sem aspas olhando para a sua forma: se corresponde ao padrão de um booleano é um booleano, se corresponde a um número é um número, e só se não corresponder a nada é que fica como texto.

É isso que torna o YAML agradável de escrever à mão e que faz da conversão um juízo. Cada cadeia da entrada tem de ser confrontada com cada padrão que o YAML resolve, e posta entre aspas se corresponder a algum. Erre para o lado seguro e a saída fica ruidosa; erre para o outro e um valor muda de tipo em silêncio.

O problema da Noruega e os seus parentes

O caso mais conhecido é uma lista de códigos de país. A Noruega é NO, e em YAML 1.1 o símbolo sem aspas NO é o booleano false. Um ficheiro de configuração que enumera países perde a Noruega e ganha um false, e nada em lado nenhum comunica um erro.

Não é uma regra estranha isolada mas uma família inteira. O YAML 1.1 lê y, Y, yes, no, on e off como booleanos, em qualquer capitalização, o que apanha um símbolo químico, a posição de um interruptor e a resposta a uma pergunta. E os resolvedores numéricos são ainda mais estranhos:

  • 12:30 é 750. O YAML 1.1 lê dígitos separados por dois pontos na base 60, por isso uma hora do dia ou uma duração torna-se um inteiro.
  • 0755 é 493. Um zero inicial significa octal em YAML 1.1 — e em YAML 1.2 o mesmo texto é o decimal 755: as duas versões discordam sobre qual número, não sobre se é número.
  • 1.10 é 1.1. Um número de versão em duas partes é um decimal, e o zero final desaparece. Uma dependência fixada em 1.10 passa a apontar para 1.1.
  • 2024-01-30 é um objeto data, não uma cadeia, porque o YAML 1.1 tem um tipo de marca temporal.
  • Uma cadeia vazia é null, tal como as palavras soltas null, Null, NULL e o til.

Nenhum destes é um erro do YAML. São os resolvedores a fazer exatamente o que prometem, sobre texto que por acaso corresponde. A única defesa é pôr entre aspas tudo o que corresponda, que é o que esta ferramenta faz e o que o painel de constatações justifica linha a linha.

Duas versões, e porque a antiga é a predefinida

O YAML 1.2 chegou em 2009 e removeu a maioria dos resolvedores surpreendentes. O seu esquema base guarda apenas true e false como booleanos, abandona a base 60 por completo e não tem tipo de marca temporal. Em 1.2, NO e 12:30 e 2024-01-30 são simplesmente cadeias.

O senão está no que realmente lê o seu ficheiro. O PyYAML implementa YAML 1.1, e o PyYAML está por trás de uma quantidade enorme de ferramentas — Ansible, clientes antigos de Kubernetes, incontáveis guiões. O yaml.v3 do Go e o js-yaml atual seguem 1.2. Assim, o mesmo documento pode ser lido de duas maneiras conforme quem o abre, e a única saída segura em todo o lado é a que está entre aspas para 1.1.

É essa a predefinição aqui. Mudar a definição para 1.2 não esconde nada: reemite com os resolvedores mais recentes e o painel de constatações encolhe, para que veja com precisão que aspas estavam lá por causa do esquema antigo. As que ficam são as de que qualquer analisador precisa.

Cadeias que partem a sintaxe, não o tipo

Um segundo grupo de cadeias tem de levar aspas por outra razão: não porque o YAML as leria como outro tipo, mas porque não seriam analisadas como texto de todo.

  • Dois pontos seguidos de um espaço terminam uma chave. «note: time: now» seria lido como uma chave note cujo valor é uma chave time.
  • Um espaço seguido de um cardinal inicia um comentário, por isso tudo o que vier a seguir desaparece.
  • Um -, ?, :, [, ], {, }, #, &, *, !, |, >, %, @ ou acento grave inicial é um carácter indicador e significa algo estrutural.
  • Um espaço inicial ou final não é conservado por um valor sem aspas, por isso « x » volta como «x».
  • Uma tabulação em qualquer parte do valor é rejeitada de imediato: o PyYAML recusa o documento inteiro em vez de o ler mal, por isso este caso falha em voz alta.

Usam-se plicas sempre que bastem, porque têm exatamente uma regra de escape — um apóstrofo escreve-se duas vezes — e leem-se melhor do que os escapes com barra invertida que as aspas duplas trazem. As duplas ficam reservadas ao que precisa mesmo de escape: caracteres de controlo, tabulações e cadeias de várias linhas que não podem usar um bloco.

Cadeias de várias linhas e o indicador de corte

Uma cadeia com mudanças de linha — um guião, um certificado, um bloco de prosa — costuma ser a razão pela qual alguém quer YAML em primeiro lugar. O JSON só a consegue escrever com escapes barra-n numa única linha longuíssima; o YAML tem o escalar de bloco literal, aberto por uma barra vertical, onde o texto aparece indentado e legível por baixo.

A subtileza está no que acontece às mudanças de linha do fim, e isso é governado pelo indicador de corte:

  • Uma barra vertical sozinha apara: por muitas mudanças finais que o bloco tenha, o valor fica exatamente com uma.
  • Uma barra vertical seguida de um menos remove: o valor não fica com nenhuma.
  • Uma barra vertical seguida de um mais conserva: o valor fica com todas.

Esta ferramenta escolhe o indicador a partir da cadeia que recebeu, para que o valor sobreviva à ida e volta exatamente. Vale a pena saber porque a predefinição — a barra sozinha — é a que se escreve à mão, e normaliza em silêncio uma cadeia que acabava em duas mudanças de linha ou em nenhuma.

Um escalar de bloco não consegue levar tudo, e onde não consegue, a saída recorre a aspas duplas e o painel de constatações diz porquê. Um retorno de carro não sobrevive, porque os escalares de bloco normalizam as quebras de linha. Uma primeira linha começada por espaço seria lida como indentação adicional e retirada. E uma linha que acaba em espaço é conservada pela especificação, mas é invisível no ecrã e a maioria dos editores remove-a ao gravar, por isso pôr aspas é mais seguro — isso é uma decisão e não uma limitação, e é comunicado como tal.

Mais do que um documento

O YAML tem algo que o JSON não tem: um ficheiro pode conter um fluxo de documentos separados por três hífenes. É o formato de um manifesto de Kubernetes, e é a razão pela qual um array JSON tantas vezes precisa de se tornar algo que não é uma sequência YAML.

O interruptor aqui emite cada elemento de um array de topo como documento próprio. E quando a entrada não é um único valor JSON mas cada linha se analisa por si, é lida como NDJSON — o formato delimitado por linhas em que chegam registos e exportações de API — e cada linha passa a documento. Essa leitura é um palpite, por isso é comunicada acima da saída em vez de ser feita em silêncio.

O recurso só se aplica quando a entrada inteira falha a análise e cada linha a consegue, coisa que um documento apenas malformado não fará. Um erro de sintaxe continua a surgir como erro de sintaxe, com a linha e a coluna em que ocorreu.

O que a conversão não consegue preservar

Duas coisas perdem-se antes de esta ferramenta sequer ver os seus dados, ambas na própria análise do JSON, e vale a pena saber quais.

Chaves duplicadas. O JSON permite que um objeto liste a mesma chave duas vezes e a maioria dos analisadores fica com a última em silêncio. O YAML proíbe duplicados de todo, por isso a saída será válida, mas o valor anterior já se perdeu — e nenhum conversor pode comunicar o que nunca recebeu.

Precisão dos inteiros. Um número JSON maior do que cerca de nove mil biliões não sobrevive à análise para um double, por isso um identificador como 12345678901234567890 volta arredondado. Não é um problema do YAML nem introduzido por esta ferramenta; acontece em todos os analisadores JSON da linguagem. Se um identificador grande importa, o seu lugar é numa cadeia dos dois lados.

Notas sobre a saída

A indentação é com espaços, sempre, porque o YAML proíbe por completo tabulações para indentar: é uma das poucas coisas em que o formato é rigoroso. Dois espaços é a convenção; quatro são oferecidos porque alguns estilos de casa os querem.

As sequências vão indentadas sob a sua chave. Tanto essa forma como a não indentada são YAML válido e significam o mesmo; a indentada é a que a maioria das pessoas escreve e a que a maioria dos editores dobra corretamente.

Um array ou objeto vazio escreve-se em estilo de fluxo como um par de parênteses ou chavetas, porque o estilo de bloco não tem maneira de exprimir o vazio: não há nada para escrever nas linhas seguintes.

A saída acaba com uma mudança de linha, e isso é funcional e não decorativo. O corte de um escalar de bloco mede-se contra a quebra de linha que se lhe segue, por isso um bloco aparado mesmo no fim de um ficheiro sem quebra final perde a quebra que devia conservar.

Perguntas frequentes

O JSON já é YAML válido?
Em YAML 1.2, sim: a especificação di-lo explicitamente e um analisador 1.2 lê um ficheiro JSON diretamente. Na prática não serve de muito, porque a razão para converter é a legibilidade — comentários, escalares de bloco, sem chavetas — e colar JSON num ficheiro YAML não lhe dá nada disso. Em YAML 1.1 não é bem verdade, o que é mais uma razão para distinguir as duas versões.
Porque é que a minha cadeia levou aspas de que parece não precisar?
Quase de certeza precisa. O YAML tipifica um valor sem aspas confrontando padrões, por isso NO, yes, off, 12:30, 0755, 1.10, 2024-01-30 e a cadeia vazia deixam todos de ser cadeias. O painel de constatações nomeia cada um e mostra o valor em que se teria tornado, para que verifique em vez de confiar. Se o seu alvo é um analisador YAML 1.2, mudar o esquema tira as que só o 1.1 precisa.
Qual é aqui a diferença entre YAML 1.1 e 1.2?
O 1.2 abandonou os resolvedores que causam quase todas as surpresas: yes/no/on/off já não são booleanos, a base 60 desapareceu e não há tipo de marca temporal. O PyYAML implementa 1.1 e continua em todo o lado, por isso a saída conservadora é a predefinida; a definição 1.2 existe para quando sabe o que vai ler o ficheiro.
Consegue converter YAML de volta para JSON?
Não, de propósito. Um leitor de YAML precisa de âncoras, aliases, etiquetas, chaves de fusão, cinco estilos de escalar e duas versões de esquema, e enganar-se subtilmente em qualquer um significa aceitar um ficheiro e devolver dados diferentes dos que continha. Essa falha é silenciosa, o que a torna pior do que não oferecer a funcionalidade.
Como obtenho um ficheiro multidocumento ao estilo do Kubernetes?
Ligue o interruptor que emite cada elemento de um array de topo como documento próprio, e o array passa a documentos separados por três hífenes. Se a sua entrada for NDJSON — um objeto JSON por linha, como muitas vezes chegam registos e exportações de API — isso é detetado automaticamente e comunicado acima da saída.
Porque é que não há comentários na saída?
Porque não havia nenhum na entrada. Os comentários são a principal coisa que o YAML tem e o JSON não, e um conversor não os pode inventar. Vale a pena lembrar também no sentido inverso: se passar um ficheiro YAML por JSON e de volta, todos os seus comentários desapareceram.
Alguma coisa do que colo é enviada para um servidor?
Não. A análise e a conversão correm inteiramente no seu navegador; nada é carregado nem registado, e funciona sem ligação à rede.