8 puntos por GN⁺ 2025-09-22 | 1 comentarios | Compartir por WhatsApp
  • Sj.h es un parser JSON ultraligero de alrededor de 150 líneas, utilizable en entornos basados en C99
  • Tiene características como cero asignaciones de memoria y estado mínimo, lo que lo hace conveniente para entornos embebidos ligeros o para minimizar dependencias
  • Adopta un enfoque de manejo directo para el parsing de números y cadenas, permitiendo que el usuario implemente libremente el procesamiento relacionado
  • Ofrece mensajes de error basados en línea:columna, lo que mejora la legibilidad y la identificación de ubicación al depurar
  • Es software de dominio público, por lo que cualquiera puede usarlo libremente, modificarlo y distribuirlo

Introducción al proyecto

  • Sj.h es un archivo de cabecera C99 que proporciona funcionalidad de parsing JSON con código mínimo, sin funciones excesivas ni asignaciones de memoria innecesarias
  • La implementación completa tiene unas 150 líneas, por lo que es adecuada para sistemas embebidos, herramientas, o casos donde se quiera reducir la dependencia de bibliotecas externas

Características principales

  • Funciona con cero asignaciones de memoria (zero-allocations) y estado mínimo (minimal state), por lo que la carga de memoria es muy baja
  • Incluye mensajes de error con información de línea:columna, facilitando ubicar los problemas que ocurren durante el parsing
  • No incluye parsing de números por defecto; el usuario puede usar el método que prefiera, como strtod, atoi
  • El parsing de cadenas también puede implementarse directamente, o construir según necesidad el manejo de Unicode surrogate pairs, entre otros
  • Todo el código fuente se ofrece como dominio público (Unlicense), por lo que puede usarse y modificarse sin restricciones de licencia

Ejemplo de uso

  • Se proporciona un ejemplo simple para parsear una cadena JSON en una estructura Rect
    char *json_text = "{ \"x\": 10, \"y\": 20, \"w\": 30, \"h\": 40 }";  
    typedef struct { int x, y, w, h; } Rect;  
    ...  
    
  • Usa una API simple y clara como sj_Reader, sj_Value, sj_reader, sj_read, sj_iter_object
  • El parsing de valores numéricos o la comparación de clave-valor deben implementarse manualmente (no viene builtin)
  • En la carpeta demo se pueden revisar varios ejemplos de uso adicionales

Licencia

  • Sj.h es software de dominio público que cualquiera puede usar sin restricciones
  • Para más detalles, consulta el archivo LICENSE

Otros

  • Como el código y la estructura de carpetas son simples, puede usarse de inmediato sin configuración ni pasos de compilación adicionales
  • Es independiente, no tiene dependencias externas, y resulta adecuado sobre todo para entornos donde se requiere ligereza y facilidad de uso

1 comentarios

 
GN⁺ 2025-09-22
Opiniones en Hacker News
  • La razón por la que me gusta el trabajo de este autor es que, en su mayoría, son bibliotecas de un solo archivo escritas en ANSI C o Lua, enfocadas en su alcance, con una interfaz fácil de usar y una documentación excelente; también me gusta su licencia de software libre. Además de este proyecto, hay varias otras obras suyas que me gustan: log.c (una biblioteca simple de logging en C99), microui (una biblioteca de UI inmediata muy pequeña), fe (un pequeño lenguaje embebible), microtar (una biblioteca liviana de tar), cembed (una herramienta para incrustar archivos en headers de C), ini (una biblioteca liviana para archivos de configuración .ini), json.lua (una biblioteca liviana de JSON para Lua), lite (un editor de texto liviano hecho en Lua), cmixer (un mezclador de audio portable para juegos) y uuid4 (una pequeña biblioteca uuid4 implementada en C)
    • Yo meto log.c en mis proyectos en C cada vez que puedo; no sabía que el autor había hecho tantas bibliotecas, pero log.c de verdad es muy fácil de manejar y la puedo recomendar
    • Usé la biblioteca Lume cuando hacía juegos con LOVE2D; de hecho, me crucé con el autor unas cuantas veces en una sala de chat de IRC, y una vez le dije que una de sus ideas era mala, pero al verla de nuevo resultó ser una buena idea; vale la pena revisar https://github.com/rxi/lume
    • Es open source, pero no es verdadero free software
  • Esta biblioteca no realiza verificación de overflow de enteros con signo en las siguientes líneas: L89, L149, L150; dependiendo de ciertos valores de entrada, puede ocurrir undefined behavior
    • A algunos desarrolladores les gusta la cultura de las bibliotecas C simples de un solo header; streamers como Tsoding son un buen ejemplo. Entienden que estas bibliotecas no están enfocadas en seguridad ni funcionalidad completa, y como no todos los proyectos son software de nivel empresarial expuesto a miles de clientes, este enfoque también puede ser válido
    • Hay un buen artículo sobre lo infladas que se vuelven las bibliotecas que intentan manejar todos los edge cases, y también una discusión relacionada. Si intentas manejar todos los errores, la complejidad se dispara, y a veces ni siquiera es responsabilidad de la biblioteca
    • El UB podría ocurrir si la profundidad de nivel supera los 2 mil millones, o en el segundo caso si el número de líneas supera los 2 mil millones. Si limitas la entrada JS a 1 GB, si entra algo mayor probablemente ya tendrás problemas más graves en otras partes del stack. Si tuviera que procesar JSON de más de 2 GB, cambiaría todos los int del código fuente a 64 bits; aun así, si la entrada supera 2^64, al final igual se romperá. No planeo revisar manualmente cada overflow de int en el código
    • Como int es de 32 bits, solo habría problema si necesitas un valor anidado con 2 mil millones de niveles por línea, un archivo de más de 2 mil millones de líneas, o una sola línea de más de 2 mil millones de caracteres
    • No creo que esta biblioteca sirva para producción
  • Esta biblioteca es bastante permisiva. No diría que eso sea algo malo, pero quienes la usen sin mirar el código deberían tener cuidado; esa es la razón principal por la que el código es tan pequeño. Si ves el ejemplo de demo del README, funciona así:<br><code>{"x",10eee"y"22:5,{[:::,,}]"w"7"h"33<br>rect: { 10, 22, 7, 33 }</code>
    • El parser normalmente asume que la entrada es válida; la validación es un problema completamente aparte que esta biblioteca no cubre. Al final, esta biblioteca cumple la función de extraer datos
    • ¿Entonces eso está mal?
  • Creo que las bibliotecas parser de JSON en general son un agujero negro de sufrimiento; cada una apunta a un caso de uso distinto y está llena de abstracciones complejas, y muchas veces aplican ambas cosas. En realidad, si implementas solo lo que necesitas para un propósito específico, no es un problema tan difícil
    • Sorprende lo complejas que se vuelven las bibliotecas modernas de JSON. La biblioteca JSON de un solo header en C++ de nlohmann, que antes era realmente simple,<br>* ya tiene 13 años<br>* todavía recibe pull requests que se hacen merge activamente<br>* tiene 122 millones de pruebas unitarias<br>aun así ellos mismos admiten que no es la más rápida; si de verdad necesitas velocidad, recomiendan simdjson. Es mejor no pensar en hacer tu propia biblioteca parser de JSON: puedes llegar al 90% en 45 minutos, pero para el 10% restante hacen falta miles de horas de trabajo de diez mil personas
    • Hay un recurso famoso llamado Parsing JSON is a Minefield (2016) https://seriot.ch/projects/parsing_json.html
    • También es raro ver un diseño tan neutral como el de esta biblioteca: simplemente va devolviendo claves e ítems de arrays mientras distingue tipos usando string slices
    • Este proyecto presume zero-allocation y almacenamiento mínimo de estado, pero para el tipo más usado, como strings, al final sí necesitas asignación; es una alternativa muy distinta a mi problema
    • Espero que las S-Expressions sigan siendo queridas
  • Está interesante, pero me pregunto cuántas pruebas de conformidad pasa esta biblioteca https://github.com/nst/JSONTestSuite
    • Desde el punto de vista de validación, esta biblioteca no parece muy estricta. Por ejemplo, acepta indistintamente ] o } para cerrar objetos o arrays, y también reconoce \v como whitespace, así que es más permisiva que el estándar. Lo correcto sería verla como un “extractor de datos para JSON válido”. Aun así, puede dar flojera tener que escribir por tu cuenta un parser de strings o números, sobre todo cuando necesitas acordar con el productor un subconjunto de la gramática JSON
    • Sería realmente útil crear pruebas de conformidad basadas en implementaciones reales: generar un conjunto de pruebas que sea exactamente el subconjunto o superconjunto que coincide con el comportamiento de una biblioteca parser específica. Así se evita el riesgo de vulnerabilidades por dos parsers que interpretan el mismo JSON de forma distinta, como un bypass de autenticación
    • Pregunta real: ¿soporta objetos anidados?
  • Se ve como un parser a medias; como no convierte los números a float o int, parece que uno mismo tendría que agregar esa parte. Aun así, me impresiona y sí tendría disposición a usarlo; solo quería señalar ese punto
  • Me pregunto si C99 especifica que esta estructura se inicializa en cero por defecto, o si simplemente faltó = { 0 }; viendo este código relacionado, parece que r->depth podría coincidir por casualidad con depth dentro del loop de sj__discard_until sin haber sido inicializado
  • Me pregunto cuál es el propósito de usar una biblioteca así, si ya parece haber muchas bibliotecas JSON excelentes; ¿es una herramienta educativa?
    • Es fácil de integrar en una base de código existente, tiene poco peso en tamaño, no usa asignación en heap y no usa la stdlib (solo incluye stdbool.h y stddef.h para definiciones de tipos), no tiene juegos raros de templates de C++ y su API es intuitiva. Una biblioteca en C que cumpla con todos esos requisitos es realmente rara, y en C++ todavía más
    • Resulta atractivo poder parsear sin overhead ni asignaciones; es útil cuando quieres extraer solo ciertas propiedades de dumps grandes de JSON, como los de Wikidata
    • Se puede usar directamente en CPUs embebidos; incluso ahora parece que hasta se podría correr un servidor API en algo como un vape
    • Como el código es pequeño, también es más fácil de auditar en proyectos con requisitos estrictos de seguridad, y el cumplimiento de licencia es muy simple (no hace falta texto de aviso)
    • Puede ser una buena base de código para principiantes o para quien quiera hacer parsing sencillo; sirve cuando necesitas un código con huella pequeña en proyectos hobby chicos con procesadores limitados, aunque en esos casos probablemente preferiría un formato como TOML
  • ¡Está genial! La próxima vez que necesite un parser JSON en C pienso revisarlo. Yo he usado cJSON (https://github.com/DaveGamble/cJSON) con buenos resultados durante años; antes de eso usaba wjelement (https://github.com/netmail-open/wjelement), pero al final cambié por algunos problemas (ya no recuerdo el motivo exacto porque fue hace tiempo)
  • Se siente un poco forzado llamarle parser a esto; en general parece más cercano a un lexer