8 pontos por GN⁺ 2025-09-22 | 1 comentários | Compartilhar no WhatsApp
  • Sj.h é um parser JSON ultraleve com cerca de 150 linhas, utilizável em ambientes baseados em C99
  • Tem características como alocação zero de memória e estado mínimo, sendo útil para ambientes embarcados leves ou quando se deseja minimizar dependências
  • Adota uma abordagem de tratamento direto para o parsing de números e strings, permitindo que o usuário implemente livremente o processamento relacionado
  • Oferece suporte a mensagens de erro baseadas em linha:coluna, aumentando a legibilidade e a identificação da posição durante o debug
  • Como software em domínio público, qualquer pessoa pode usar livremente, modificar e distribuir

Introdução ao projeto

  • Sj.h é um arquivo de cabeçalho C99 que fornece parsing de JSON com apenas o mínimo de código, sem recursos excessivos nem alocações de memória desnecessárias
  • A implementação inteira tem cerca de 150 linhas, sendo adequada para sistemas embarcados, ferramentas ou casos em que se deseja reduzir a dependência de bibliotecas externas

Principais características

  • Funciona com zero-allocations e minimal state, resultando em consumo de memória muito baixo
  • As mensagens de erro incluem informações de linha:coluna, facilitando identificar a posição de problemas que ocorram durante o parsing
  • O parsing de números não é fornecido por padrão; o usuário pode usar a abordagem que preferir, como strtod ou atoi
  • O parsing de strings também pode ser implementado diretamente, incluindo tratamento de Unicode surrogate pairs conforme necessário
  • Todo o código-fonte é fornecido em domínio público (Unlicense), permitindo uso e modificação sem restrições de licença

Exemplo de uso

  • É fornecido um exemplo simples de parsing de uma string JSON para uma estrutura Rect
    char *json_text = "{ \"x\": 10, \"y\": 20, \"w\": 30, \"h\": 40 }";  
    typedef struct { int x, y, w, h; } Rect;  
    ...  
    
  • Usa uma API simples e direta como sj_Reader, sj_Value, sj_reader, sj_read e sj_iter_object
  • O parsing de valores numéricos e a comparação de chave-valor devem ser implementados diretamente pelo usuário (não há builtin)
  • É possível conferir vários exemplos adicionais de uso na pasta demo

Licença

  • Sj.h é um software em domínio público que qualquer pessoa pode usar sem restrições
  • Para mais detalhes, consulte o arquivo LICENSE

Outros

  • O código e a estrutura de pastas são simples, permitindo uso imediato sem configuração separada ou processo de build
  • É independente, não tem dependências externas e é adequado principalmente para ambientes que exigem leveza e facilidade de uso

1 comentários

 
GN⁺ 2025-09-22
Comentários no Hacker News
  • Gosto do trabalho deste autor porque, na maioria das vezes, são bibliotecas de arquivo único escritas em ANSI C ou Lua, focadas no escopo, com interface fácil de usar e documentação excelente; também gosto da licença de software livre. Além deste projeto, há outros trabalhos dele de que gosto: log.c (biblioteca simples de logging em C99), microui (biblioteca de UI imediata bem pequena), fe (uma linguagem pequena que pode ser embutida), microtar (biblioteca leve de tar), cembed (ferramenta para embutir arquivos em headers C), ini (biblioteca leve para arquivos de configuração .ini), json.lua (biblioteca leve de JSON para Lua), lite (editor de texto leve feito em Lua), cmixer (mixer de áudio portátil para jogos) e uuid4 (pequena biblioteca uuid4 em C)
    • Eu coloco log.c em praticamente todo projeto em C; não sabia que o autor tinha feito tantas bibliotecas. log.c é realmente muito fácil de usar, então posso recomendar.
    • Eu usava a biblioteca Lume quando fazia jogos em LOVE2D. Na verdade, já encontrei o autor algumas vezes em salas de chat de IRC e, certa vez, cheguei a dizer que uma ideia dele era ruim. Mas, olhando de novo, era uma boa ideia. Vale conferir: https://github.com/rxi/lume
    • É open source, mas não é software livre de verdade.
  • Esta biblioteca não faz verificação de overflow de inteiro com sinal nas seguintes linhas: L89, L149, L150. Dependendo da entrada, pode ocorrer undefined behavior.
    • Alguns desenvolvedores gostam dessa cultura de bibliotecas C simples de header único; streamers como Tsoding são um bom exemplo. Entendo que essas bibliotecas não focam em segurança nem em funcionalidade completa. Nem todo projeto é software de nível empresarial exposto a milhares de clientes, então essa abordagem também pode ser válida.
    • Há um bom texto sobre o inchaço de bibliotecas que lidam com todos os edge cases, e também uma discussão relacionada. Tentar tratar todos os erros faz a complexidade disparar, e às vezes isso nem deveria ser responsabilidade da biblioteca.
    • UB só aconteceria com profundidade de nível acima de 2 bilhões ou, no segundo caso, com mais de 2 bilhões de linhas. Se você limitar a entrada JS a 1 GB, ao passar disso provavelmente já haverá problemas maiores em outras partes da pilha. Se eu precisasse processar JSON acima de 2 GB, trocaria todos os int do código-fonte para 64 bits; ainda assim, se a entrada passasse de 2^64, no fim morreria do mesmo jeito. Não pretendo sair verificando overflow de int em cada parte do código.
    • Como int é de 32 bits, só haveria problema se cada linha tivesse aninhamento de 2 bilhões de níveis, se o arquivo tivesse mais de 2 bilhões de linhas, ou se uma única linha tivesse mais de 2 bilhões de caracteres.
    • Acho que não daria para usar esta biblioteca em produção.
  • Esta biblioteca é bastante permissiva. Não diria que isso é ruim, mas quem for usá-la sem olhar o código precisa tomar cuidado. Essa é a principal razão de o código ser tão pequeno. Pelo exemplo de demonstração no README, ela funciona assim:<br><code>{"x",10eee"y"22:5,{[:::,,}]"w"7"h"33<br>rect: { 10, 22, 7, 33 }</code>
    • O parser geralmente assume que a entrada é válida. O trabalho de validação é um problema completamente separado, que esta biblioteca não cobre. No fim, ela serve para extrair dados.
    • Então isso está errado?
  • Acho que bibliotecas de parser JSON, em geral, são um buraco negro de sofrimento. Cada uma tenta servir a um lugar diferente, e muitas estão cheias de abstrações complexas; muitas vezes, ambas as coisas ao mesmo tempo. Na verdade, se você implementar só o necessário para um objetivo específico, nem é um problema tão difícil assim.
    • É surpreendente como bibliotecas JSON modernas ficam complexas. A antiga biblioteca JSON de header único em C++ do nlohmann, que era realmente simples,<br>* já tem 13 anos<br>* ainda recebe pull requests mesclados ativamente<br>* tem 122 milhões de testes unitários<br>e, mesmo assim, eles próprios admitem que ela não é a mais rápida. Se você realmente precisa de velocidade, recomendam simdjson. Melhor nem pensar em escrever sua própria biblioteca de parser JSON: você consegue chegar aos 90% em 45 minutos, mas para os 10% restantes precisa de milhares de horas de trabalho de dez mil pessoas.
    • Há um material famoso chamado Parsing JSON is a Minefield (2016): https://seriot.ch/projects/parsing_json.html
    • Parece raro ver um design tão neutro quanto o desta biblioteca: ela só vai retornando chaves e itens de array iterativamente, distinguindo tipos por string-slice.
    • Este projeto se vende como zero-allocation e de armazenamento mínimo de estado, mas para os tipos mais comuns, como strings, no fim ainda é preciso alocar. É uma alternativa bem diferente do meu problema.
    • Espero que S-Expressions ainda continuem sendo amadas.
  • Interessante, mas fico curioso para saber quantos testes de conformidade esta biblioteca passa: https://github.com/nst/JSONTestSuite
    • Em termos de validação, esta biblioteca não parece muito rígida. Por exemplo, aceita tanto ] quanto } para fechar objetos ou arrays, e também reconhece \v como whitespace, então é mais permissiva do que o padrão. O mais correto seria vê-la como um "extrator de dados para JSON válido". Ainda assim, pode ser chato escrever por conta própria parsers de string ou número, especialmente quando é preciso alinhar com o produtor sobre um subconjunto da gramática JSON.
    • Seria muito útil criar testes de conformidade baseados em implementações reais: montar um conjunto de testes que seja exatamente o subconjunto/superconjunto compatível com o comportamento de uma biblioteca de parsing específica. Assim, você evita riscos de vulnerabilidades causadas por dois parsers interpretando o mesmo JSON de formas diferentes (por exemplo, bypass de autenticação).
    • Pergunta sincera: ele suporta objetos aninhados?
  • Parece meio que um parser pela metade: ele não converte números para float ou int, então você provavelmente teria de adicionar essa parte por conta própria. Ainda assim, fiquei impressionado e usaria; estou só apontando isso.
  • Fico em dúvida se o C99 especifica que esta struct é inicializada com zero por padrão, ou se faltou um = { 0 }. Olhando este código relacionado, parece que r->depth poderia acabar coincidindo por acaso com depth no loop sj__discard_until sem ter sido inicializado.
  • Fico pensando qual é o objetivo de usar uma biblioteca assim, já que parece haver tantas bibliotecas JSON excelentes por aí. É uma ferramenta educacional?
    • É fácil de integrar a uma base de código existente, tem baixo custo em tamanho, não usa alocação no heap e nem stdlib (inclui só stdbool.h e stddef.h para definir tipos), não tem maluquice de template em C++, e a API é intuitiva. Bibliotecas C que atendam a todos esses requisitos são realmente raras; em C++, mais raras ainda.
    • O atrativo é fazer parsing sem overhead nem alocações. É útil quando você quer extrair apenas certas propriedades de dumps grandes de JSON (por exemplo, dumps do Wikidata).
    • Dá para usar direto em CPU embarcada; hoje em dia parece que até em vape já dá para rodar servidor de API.
    • Como o código é pequeno, também fica mais fácil revisá-lo em projetos com requisitos rigorosos de segurança, e a conformidade de licença é muito simples (não precisa de aviso de atribuição).
    • Pode ser uma boa base de código para iniciantes consultarem ou para quem quer fazer parsing simples. Também serve quando se precisa de uma pegada de código pequena em projetos hobby pequenos com processador limitado; ainda assim, nesse caso, eu provavelmente preferiria um formato como TOML.
  • Legal! Da próxima vez que eu precisar de um parser JSON em C, vou dar uma olhada. Eu usei cJSON(https://github.com/DaveGamble/cJSON) por anos com bons resultados. Antes disso, usei wjelement(https://github.com/netmail-open/wjelement), mas depois mudei por causa de alguns problemas (não lembro mais os motivos exatos, faz muito tempo).
  • Chamar isto de parser parece um pouco forçado; no geral, parece mais próximo de um lexer.