1 pontos por GN⁺ 21 시간 전 | 1 comentários | Compartilhar no WhatsApp
  • YAML 1.2 é um formato de serialização baseado em indentação para arquivos de configuração aninhados escritos por humanos, e precisa ser distinguido das críticas de instabilidade herdadas de experiências antigas com PyYAML
  • Os formatos de arquivo de configuração evoluíram como uma resposta às limitações da geração anterior, como a estrutura plana do INI, a verbosidade do XML e a ausência de comentários e strings multilinha no JSON
  • TOML é claro em estruturas rasas como pyproject.toml e Cargo.toml, mas em estruturas aninhadas mais profundas aumenta o custo de leitura e edição por causa da repetição de caminhos e dos arrays de tabelas
  • O YAML 1.2 Core Schema trata yes, no, on, off, y, n como strings e remove números sexagesimais e timestamps como tipos centrais, reduzindo problemas antigos de inferência implícita de tipos
  • py-yaml12 é um parser e formatador YAML 1.2 para Python que, por meio de uma implementação em Rust, oferece padrões seguros, 100% de conformidade com o yaml-test-suite e desempenho comparável à extensão C do PyYAML

Problema

  • Nos últimos anos, o clima em torno dos formatos de arquivo de configuração migrou para a ideia de que YAML é ruim e TOML é bom, mas avaliar YAML exige considerar ao mesmo tempo a história, as mudanças da especificação e o estado das ferramentas em 2026
  • As críticas ao YAML foram razoáveis por muito tempo, e até usuários cuidadosos passaram por comportamentos inesperados, mas a especificação mudou e o ecossistema de ferramentas está correndo atrás
  • As críticas atuais ao YAML só podem ser entendidas quando se observa também a linhagem dos formatos de configuração, um fluxo em que se repetem debates nos quais o formato seguinte corrige os excessos do anterior

Breve história dos formatos de arquivo de configuração

  • Arquivos INI surgiram no começo dos anos 1980 com o MS-DOS e o Windows inicial, como um formato plano, fácil de ler e editável por humanos, com pares chave-valor, seções entre colchetes e comentários com ponto e vírgula
  • O INI bastava para demandas da época, como configuração de drivers de dispositivo, definição de caminhos de fontes e ajustes de aplicações, mas tinha limitações estruturais: não permitia aninhamento com mais de um nível e não havia especificação oficial, então cada parser implementava seu próprio dialeto
  • O XML foi amplamente adotado no fim dos anos 1990 no mundo do software corporativo e oferecia hierarquia arbitrária, schemas, namespaces, transformações e uma estrutura autodescritiva, mas na prática os arquivos de configuração ficaram tão verbosos que se tornaram difíceis de manter manualmente
  • O JSON surgiu como uma resposta mais leve ao XML e, com base na simplicidade dos literais de objeto do JavaScript, substituiu o XML em APIs web no fim dos anos 2000 e começo dos anos 2010, mas como formato de configuração tinha limitações como ausência de comentários, ausência de strings multilinha e proibição de vírgula final
  • YAML surgiu em 2001 e TOML em 2013 para preencher o espaço deixado pelo JSON; o YAML trouxe uma sintaxe baseada em indentação com aninhamento arbitrário, múltiplos documentos, referências e tipos definidos pelo usuário, enquanto o TOML buscou um “INI padronizado” com tipos explícitos e especificação oficial
  • Cada nova geração de formato partiu dos excessos da anterior, e boa parte dos problemas atuais atribuídos ao YAML vêm menos do desenho do formato em si e mais de versões específicas da especificação e dos parsers presos a elas

Problemas que justificavam a oposição ao YAML

  • As críticas ao YAML não eram inventadas: vinham de experiências reais que programadores enfrentaram ao longo de muitos anos
  • O problema mais famoso, o da Noruega, é o comportamento do YAML 1.1 em que o escalar sem aspas NO é interpretado como o booleano false, fazendo com que no em uma lista de códigos de país vire um valor falso
    countries:
      - dk
      - fi
      - is
      - no
      - se
    
    ["dk", "fi", "is", false, "se"]
    
  • A mesma regra também se aplicava a yes, on, off, y, n e várias combinações de maiúsculas e minúsculas; mapeamentos de porta como 22:22 eram lidos como inteiros sexagesimais, números de versão como 10.23 viravam ponto flutuante em vez de string, e valores que pareciam datas eram interpretados como timestamps
  • Em código de ciência de dados e machine learning, nomes de variáveis naturais como n e y também podiam ser interpretados não como chaves string, mas como chaves False e True sob as regras de booleano implícito do YAML 1.1
    {"variables": {"x": "features", False: "sample_size", True: "target"}}
    
  • A inferência implícita de tipos agressiva do YAML 1.1 pretendia melhorar a legibilidade ao ler true sem aspas como booleano, mas na prática deixava os arquivos de configuração mais imprevisíveis
  • Arquivos de configuração costumam ser editados com pouca frequência e muitas vezes por alguém que não foi o autor original; assim, um parsing silenciosamente errado pode se propagar por meses por todo o sistema, tornando esse um dos contextos menos tolerantes a comportamentos inesperados
  • A complexidade da especificação completa do YAML também pesava; a especificação YAML 1.2.2 é um documento considerável, com 10 capítulos e uma estrutura de seções numeradas em quatro níveis
  • Há muitas formas de representar strings multilinha, e âncoras e aliases criam um sistema de referências poderoso, mas conceitualmente mais pesado do que o necessário para a maioria das tarefas de configuração
  • Problemas de segurança na desserialização de objetos baseada em tags, especialmente a vulnerabilidade de yaml.load() em Python, tornaram-se um vetor de ataque bem conhecido, e esse tipo de crítica era válido para o YAML 1.1 e para o ecossistema de ferramentas em torno dele

O que o TOML faz bem

  • TOML é um formato limpo, legível e sem ambiguidades em estruturas de configuração planas ou rasas
  • A sintaxe do TOML é familiar para quem já viu arquivos INI, mas acrescenta tipos explícitos, especificação oficial e suporte a tabelas aninhadas por meio de chaves separadas por ponto
  • pyproject.toml e Cargo.toml são exemplos em que o TOML se encaixa bem, porque normalmente têm seções bem definidas e profundidade de um ou dois níveis
  • No TOML, strings sempre vêm entre aspas, então não há ambiguidade sobre no ser booleano ou palavra; inteiros, ponto flutuante e datas são tipos de primeira classe, e comentários funcionam da forma esperada
  • A adoção do PEP 518 no ecossistema de empacotamento Python e o uso do Cargo pela comunidade Rust sustentam a ideia de que o TOML é uma escolha adequada nesse escopo de problemas
  • A especificação do TOML é curta o bastante para que um programador competente escreva um parser compatível em um fim de semana, o que contribuiu para um ecossistema de parsers grande, bem testado e consistente
  • O TOML não tem um problema equivalente à divisão de versões entre YAML 1.1 e 1.2; TOML 1.0 é TOML 1.0 em qualquer lugar

Onde o TOML começa a pesar

  • Quando um arquivo de configuração precisa expressar profundidade, a estrutura aninhada do TOML depende de cabeçalhos de seção com chaves separadas por ponto ([servers.alpha]) ou arrays de tabelas ([[products]]), e isso fica mais difícil de ler à medida que o aninhamento cresce
  • Martin Vejnár, do PyTOML, ao recusar que sua biblioteca se tornasse uma dependência do pip, citou a experiência de que, conforme o schema de configuração ficou mais complexo, a sintaxe TOML passou a parecer feia e difícil de ler
  • No YAML, a indentação transmite a hierarquia de imediato; no TOML, é preciso repetir o caminho completo em cada cabeçalho de seção
    services:
      web:
        image: nginx:latest
        environment:
          DB_HOST: postgres
          DB_PORT: 5432
        resources:
          limits:
            memory: 512M
            cpu: "0.5"
    
    [services.web]
    image = "nginx:latest"
    
    [services.web.environment]
    DB_HOST = "postgres"
    DB_PORT = 5432
    
    [services.web.resources.limits]
    memory = "512M"
    cpu = "0.5"
    
  • O leitor de TOML precisa reconstruir mentalmente a árvore a partir de uma sequência de nomes qualificados achatados, e medições citadas na documentação do StrictYAML mostram que, para representar os mesmos dados, um arquivo TOML usa cerca de 50% mais caracteres por causa dos prefixos de caminho repetidos
  • O Python mostrou há muito tempo que usar indentação como estrutura não é uma fraqueza, mas uma força, ao eliminar uma classe de bugs em que a estrutura visual e a estrutura sintática divergem
  • O YAML herda essa vantagem da estrutura por indentação, mas o TOML não exige indentação, então a relação entre chaves e tabelas contidas existe apenas nos cabeçalhos de seção, e não no posicionamento físico do arquivo
  • Em arquivos de configuração profundamente aninhados, o TOML é difícil de escanear e difícil de editar com confiança

Mudanças no YAML 1.2

  • A especificação YAML 1.2 foi publicada em 2009, e a revisão de esclarecimento 1.2.2 foi concluída em outubro de 2021
  • No YAML 1.2 Core Schema, apenas true, false e True, False, TRUE, FALSE são reconhecidos como booleanos; yes, no, on, off, y, n são strings comuns
  • Literais numéricos sexagesimais que causavam o problema de 22:22 foram removidos por completo, e timestamps deixaram de ser um tipo central, de modo que no Core Schema um 2026-05-05 sem aspas não é uma data detectada automaticamente, mas uma string
  • O JSON passou a ser um subconjunto estrito e correto do YAML 1.2, e qualquer documento JSON válido é parseado da mesma forma em YAML
  • As regras de interpretação de tags ficaram mais rígidas e claras, e a própria especificação passou a ser redigida de forma mais clara e mantida publicamente no GitHub
  • O YAML que as pessoas criticavam era o YAML 1.1; a especificação que domina a linguagem hoje é o YAML 1.2, mais seguro e previsível
  • O problema é que a experiência do usuário com YAML é mediada não pela especificação, mas pelo parser; e, para a maioria dos usuários Python, esse parser é o PyYAML, que implementa YAML 1.1 e não mudou a semântica central desde 2006

Panorama dos parsers YAML em Python

  • PyYAML é a biblioteca YAML padrão de fato em Python; ela encapsula a biblioteca C LibYAML por desempenho e também oferece um caminho alternativo em Python puro
  • O PyYAML é baixado milhões de vezes por semana e é dependência de inúmeros pacotes, mas implementa YAML 1.1, sendo a raiz da experiência “o YAML parseou código de país como booleano” no ecossistema Python
  • O repositório do PyYAML tem mais de 200 issues abertas e mais de 100 pull requests abertas; o projeto continua mantido, mas avança lentamente, e a transição para uma semântica YAML 1.2 em uma versão principal ainda não aconteceu
  • ruamel.yaml oferece suporte a YAML 1.2 e recursos de edição round-trip que preservam comentários, flow style e ordem das chaves, sendo uma opção muito mais poderosa que o PyYAML quando é necessário preservar comentários ou editar com consciência de formatação
  • O ruamel.yaml é consideravelmente mais lento que o caminho rápido em C do PyYAML porque seu modo round-trip padrão é em grande parte implementado em Python puro, e seu histórico de empacotamento, com problemas de namespace package e cadeia de dependências, já confundiu pipelines de distribuição
  • StrictYAML implementa um subconjunto intencional do YAML, removendo toda inferência implícita de tipos, tags, âncoras e flow style
  • Filosoficamente, o StrictYAML está mais perto do TOML do que do YAML completo: é um formato seguro e simples que usa a sintaxe indentada do YAML, mas é exclusivo de Python, não tem implementações em outras linguagens e não busca conformidade com a especificação
  • Nesse cenário, faltava uma biblioteca que oferecesse conformidade completa com YAML 1.2, alto desempenho e fácil substituição da interface básica do PyYAML

Apresentando o py-yaml12

  • py-yaml12 é um parser e formatador YAML 1.2 para Python, implementado em Rust para velocidade e correção
  • O py-yaml12 é construído sobre o crate Rust YAML saphyr e oferece uma API pequena e focada com parse_yaml(), read_yaml() para carregamento, e format_yaml(), write_yaml() para serialização
  • Simplicidade

    • Na maioria dos casos de uso, o design trabalha do começo ao fim apenas com tipos embutidos básicos do Python, como dict, list, int, float, str e None
    • No caminho comum, não há classes especiais de documento nem tipos de nó personalizados, e como YAML 1.2 é um superconjunto de JSON, todo JSON válido é parseado da mesma forma
    • O py-yaml12 alcança 100% de conformidade com o yaml-test-suite, coleção comunitária de casos-limite e testes de conformidade
    • Em uma lista regions, no vira silenciosamente False no comportamento YAML 1.1 do PyYAML, mas no comportamento YAML 1.2 do py-yaml12 permanece como a string "no", como exige a especificação
    • A API de arquivos também é direta: ao gravar um dicionário Python em disco e lê-lo de volta, obtém-se o mesmo objeto em um round-trip sem perdas
    • Para recursos avançados do YAML, como valores com tags, a biblioteca fornece um tipo wrapper Yaml, mas ele é opcional nas tarefas comuns de configuração
  • Segurança

    • Os padrões do py-yaml12 aumentam não só a conveniência, mas também a segurança; a abordagem oposta do PyYAML trata tags como comandos, com o risco de executar código Python arbitrário apenas ao ler um arquivo YAML
    • Se um arquivo YAML criar um alias para o namespace de tags Python object-apply do PyYAML e for lido com yaml.load(f, Loader=yaml.Loader), o código Python é executado antes mesmo de retornar um dicionário aparentemente normal
    • No exemplo, o resultado do parsing parece ser {'debug': False, 'retries': 3}, mas antes disso a variável de ambiente YAML_PAYLOAD_RAN já foi definida como '1', um problema em que não dá para perceber a execução olhando só o resultado
    • O py-yaml12 não executa tags que não foram explicitamente escolhidas e as mantém como dados; tags não processadas são retornadas em um wrapper Yaml contendo valor e tag
  • Velocidade

    • Os benchmarks do py-yaml12 comparam desempenho de leitura e escrita, em arquivos de kilobytes a megabytes, com o caminho padrão em Python puro do PyYAML, com CSafeLoader e CSafeDumper baseados em LibYAML, e com o ruamel.yaml
    • Como a lógica central de parsing e formatação é implementada em Rust compilado, e não em Python interpretado, o py-yaml12 entrega desempenho comparável à extensão C do PyYAML mantendo conformidade completa com YAML 1.2
    • Hoje há poucas opções em bibliotecas Python que ofereçam ao mesmo tempo conformidade completa com YAML 1.2 e desempenho do nível das extensões C do PyYAML

Conclusão

  • O debate comum entre YAML e TOML se parece mais com uma discussão contra uma forma de YAML problemática que já não existe mais; as críticas eram reais, mas históricas
  • Quando se critica YAML, muitas vezes o alvo é o YAML 1.1 experimentado por meio do PyYAML, e não a especificação nem um YAML 1.2 corretamente implementado
  • O TOML continua sendo uma boa escolha para configurações rasas e planas, e pyproject.toml se encaixa bem nesse papel, mas a afirmação de que YAML é inerentemente inseguro ou imprevisível não se sustenta diante de um parser compatível com YAML 1.2
  • A pergunta importante não é qual formato é o melhor em abstrato, mas qual formato e qual ferramenta se encaixam bem em uma tarefa específica
  • Para arquivos de configuração complexos, aninhados e escritos por humanos, YAML 1.2 com um parser moderno é uma resposta forte, e os formatos evoluem justamente corrigindo as arestas da geração anterior
  • Em Python, é possível experimentar uma experiência moderna e conforme à especificação com pip install py-yaml12
  • No ambiente R, r-yaml12 oferece os mesmos benefícios: conformidade completa com YAML 1.2, desempenho baseado em Rust e padrões seguros, assim como o pacote Python

1 comentários

 
Opiniões no Lobste.rs
  • A explicação de que o YAML surgiu para preencher lacunas do JSON é estranha. Pelo que eu lembrava, o YAML veio da comunidade Perl e talvez seja da mesma época do JSON ou até mais antigo
    Procurando a história real, um texto interessante de Ingy dot Net, um dos criadores do YAML, situa a origem entre novembro de 1999 e janeiro de 2001
    Já este texto sobre a história do JSON mostra que uma forma primitiva de JSON apareceu em abril de 2001, no envio de mensagens do servidor para o cliente usando iframes ocultos e tags <script>, e só virou um formato independente em 2002, quando Douglas Crockford publicou o json.org
    Portanto, o YAML veio um pouco antes do JSON ou, numa leitura generosa, os dois se desenvolveram praticamente ao mesmo tempo. Não é correto dizer que ele foi uma reação ao JSON ou que surgiu para cobrir falhas do JSON; na prática, o YAML foi uma reação ao XML. O JSON também nasceu de uma necessidade prática de embutir dados diretamente em tags <script>, mas é difícil negar que ele ganhou popularidade por se firmar como uma alternativa mais simples ao XML
    O próprio texto também parece ter traços de ter sido escrito por LLM, e o projeto apresentado parece ter sido feito com vibe coding

    • Está certo dizer que o YAML originalmente foi uma reação e alternativa ao XML
    • Ao criticar a complexidade do YAML e depois elogiar a nova especificação do YAML, também senti cheiro de LLM. Critica a especificação 1.2.2 por ser longa já no quarto nível de profundidade, mas depois diz que a 1.2.2 continua grande, porém bem menos complexa que a 1.1
      Cada frase isoladamente parece plausível, mas no conjunto fica confuso. E também é meio estranho defender o TOML por ser uma linguagem com especificação enquanto critica o YAML por ter uma especificação complexa. Dá até a impressão de que o YAML não tinha especificação e o TOML tinha
    • Acrescentando mais uma coisa a essa história: Crockford estava originalmente lidando com o Data-E, um subconjunto de E, e o Data-E representava apenas dados simples. Em vez de promover sua própria linguagem, os autores de E migraram para tornar o ECMAScript capability-safe, e várias ideias do E, como WeakMap, acabaram entrando no ECMAScript
      O JSON é, na prática, algo bem próximo de um Data-E com cara de ECMAScript. Na página arquivada do Data-E, dá para ver que eles também estavam reagindo ao XML
    • Numa releitura mais benevolente do texto, dá para dizer que não foi o desenvolvimento do YAML em si, mas sim a adoção do YAML, que foi uma reação às limitações do JSON. Eu mesmo só fui conhecer YAML depois que o JSON já estava amplamente difundido. Ainda assim, não há dados que sustentem essa percepção
  • Usando tabelas inline, o exemplo “ruim” de TOML fica assim

    [services.web]  
    image = "nginx:latest"
    
    environment = {  
      DB_HOST = "postgres",  
      DB_PORT = 5432,  
    }
    
    resources.limits = {  
      memory = "512M",  
      cpu = "0.5",  
    }  
    

    Fica bem razoável de ver e, se a discussão for contagem de caracteres, até parece mais curto que o exemplo em YAML

  • Como o objetivo do texto é defender o YAML, talvez isso esteja fora de escopo, mas eu gostaria que também tratasse do TOML 1.1 e dos novos recursos de tabelas inline. Eles resolvem três coisas de que eu não gostava no JSON: nomes de chave sem aspas, strings de comentário e suporte a vírgula final

    • Dizer que “as críticas ao YAML agora atacam um formato que já não existe em sua forma problemática” pode até fazer sentido, mas depois rebater versões antigas do TOML fica meio estranho
      O Python 3.15 oferece suporte ao TOML 1.1, e a adoção do TOML 1.1 parece muito melhor do que a do YAML 1.2. Atualizar um parser de TOML 1.0 para 1.1 provavelmente é quase trivial, mas subir de YAML 1.1 para 1.2 não parece ser. Nem consigo encontrar direito uma lista de mudanças em yaml.org; só vejo duas especificações enormes
      Coisas como o “Norway Problem” são quase uma nota de rodapé entre os motivos de eu não gostar de YAML; o principal é que ele é difícil de editar, usa espaço em branco significativo e, no geral, é bastante complexo
  • Acho que YAML, JSON e TOML têm cada um o seu espaço. Um espaço que eu senti vazio por muito tempo foi preenchido por https://kdl.dev/
    JSON = troca de dados com aninhamento arbitrário
    YAML = formato de contêiner raso para armazenar strings multilinha
    TOML = arquivo de configuração quase plano
    KDL = DSL em estilo Ruby representada como dados

    • Em projetos novos, se eu preciso de configuração, também acabei adotando KDL. Ele elimina muita sintaxe de separador irrelevante e muitas regras de indentação
    • KDL é realmente muito bom. Continuo procurando oportunidades para usar. Há situações em que, como no XML, ter atributos e nós-filho ao mesmo tempo deixa a marcação muito mais legível, e é ótimo ter essa opção com uma sintaxe leve
    • Assim que vi o exemplo pensei “isso parece SDLang”, e não era coincidência. Valeu por apresentar o KDL
  • Não gosto de YAML por causa de coisas como o Norway problem. O YAML 1.2 reduziu isso um pouco, mas por causa de strings sem aspas, strings como "", "Null", "true", "FALSE" ainda continuam sendo problemáticas, e é preciso um encoder que conheça YAML. Também não gosto da complexidade geral, mas o verdadeiro motivo de eu odiar YAML é este

    tab characters must not be used in indentation
    Quando a indentação tem significado, proibir misturar tabs e espaços ou usá-los de forma inconsistente é aceitável. A abordagem do Python 3 parece bem boa
    Indentation is rejected as inconsistent if a source file mixes tabs and spaces in a way that makes the meaning dependent on the worth of a tab in spaces; a TabError is raised in that case.
    Mas o YAML parece ser o único formato de arquivo que permite ou exige indentação e, ainda assim, não oferece suporte a tabs e espaços ao mesmo tempo
    Dá para dizer que o Make não oferece suporte a espaços, mas é possível definir .RECIPEPREFIX com um valor que não seja tab. Dá até para argumentar que, no Make, o tab não é indentação, mas sim um marcador usado pelo próprio Make

    • Não consigo achar agora, mas lembro de ter lido uma citação em que Guido van Rossum dizia que, se refizesse o Python do zero, uma das coisas que com certeza mudaria seria a proibição do caractere tab real na indentação
      O PEP-8 também recomenda fortemente não usar tabs para indentação

      Tabs should be used solely to remain consistent with code that is already indented with tabs.
      -- https://peps.python.org/pep-0008/#tabs-or-spaces

    • Para referência, a especificação do NestedText também diz isto

      Only ASCII spaces are allowed in the indentation. Specifically, tabs and the various Unicode spaces are not allowed.

  • Acho que a lógica de “o YAML não é ruim, o YAML 1.1 é que era ruim, mas a maioria ainda usa parser 1.1” não funciona tão bem quanto o texto gostaria
    Gosto de YAML usado com moderação e também fico feliz em ver surgirem novos parsers de alto desempenho para YAML 1.2, mas, se a versão “ruim” ainda é a usada pela maioria, acho que eu usaria outra coisa. Se eu não posso controlar qual parser será usado e, por isso, não posso confiar que meu YAML será interpretado corretamente, então o YAML como um todo ainda está em um estado “ruim”
    Todo mundo deveria migrar para 1.2, mas, até lá, acho razoável tratar YAML com cautela

  • Diante de GitHub Actions e Kubernetes, a frase “o debate YAML vs TOML normalmente ataca um formato que já não existe mais em sua forma problemática, e as reclamações são reais, mas históricas” me faz querer gritar

  • A defesa é forte. Mesmo assim, em documentos muito simples, o TOML é mais legível, e é mais fácil fazer as pessoas usarem TOML do que YAML
    Infelizmente, existe uma longa inércia para mudar a percepção pública dos desenvolvedores sobre ferramentas. As pessoas leem uma certa narrativa, tomam sua decisão e então migram para outra ferramenta que não cometeu erros publicamente

    • Isso deixa de valer quando a organização passa de 2 níveis de profundidade, incluindo arrays. A partir desse ponto, a indentação torna a estrutura muito mais fácil de entender
  • Queria que o parser YAML incluído no interpretador Ruby suportasse YAML 1.2.2
    Mas não sei bem como trocar a versão padrão sem quebrar o ecossistema

  • Seria bom se formatos de arquivo de configuração pudessem especificar um schema padronizado. Assim, o editor poderia olhar para qualquer arquivo de configuração e avisar sobre chaves com erro de digitação ou incompatibilidade de tipos
    Também deveria ser fácil documentar para que serve cada chave com dicas de “hover” e fornecer autocompletar de chaves válidas. Melhor ainda se houvesse suporte até para asserções ou contratos simples para detectar valores inválidos. Por exemplo, a chave "color" teria que corresponder a /#[a-fA-F0-9]{6}/
    Idealmente, isso também deveria permitir gerar automaticamente as estruturas de dados do arquivo de configuração

    • Isso é basicamente uma descrição de XML
      Existem vários formatos de especificação de validação, como XSD e Relax NG, e eu estou mais familiarizado com XML DTD, então não posso falar muito sobre os outros
    • Em arquivos JSON, é bem comum usar uma propriedade $schema na chave de nível superior para referenciar um arquivo JSON Schema que define o schema correto. É essencialmente um XSD para JSON. Bons editores normalmente conseguem oferecer autocompletar e sublinhado vermelho com base nisso
      O bom é que TOML e YAML são, em linhas gerais, JSON com sintaxe diferente, então normalmente também dá para usar arquivos de JSON Schema com eles