8 Punkte von GN⁺ 2025-09-22 | 1 Kommentare | Auf WhatsApp teilen
  • Sj.h ist ein ultraleichter JSON-Parser mit nur etwa 150 Zeilen, der in C99-basierten Umgebungen eingesetzt werden kann
  • Zu den Merkmalen gehören keine Speicherallokationen und ein minimaler Zustand, was ihn für leichtgewichtige Embedded-Umgebungen oder minimale Abhängigkeiten geeignet macht
  • Für das Parsen von Zahlen und Strings verfolgt die Bibliothek einen Ansatz mit direkter Verarbeitung, sodass Nutzer die entsprechende Logik frei selbst implementieren können
  • Unterstützung für Fehlermeldungen auf Basis von Zeile:Spalte erhöht Lesbarkeit und Positionsgenauigkeit beim Debugging
  • Als Public-Domain-Software darf sie von jedem frei verwendet, verändert und verteilt werden

Projektvorstellung

  • Sj.h ist eine C99-Header-Datei, die JSON-Parsing mit minimalem Code bereitstellt, ohne überladene Funktionen oder unnötige Speicherallokationen
  • Die gesamte Implementierung umfasst rund 150 Zeilen und eignet sich für Embedded-Systeme, Tools oder Szenarien, in denen Abhängigkeiten von externen Bibliotheken reduziert werden sollen

Hauptmerkmale

  • Arbeitet mit zero-allocations und minimal state, wodurch der Speicherbedarf sehr gering bleibt
  • Fehlermeldungen mit Zeile:Spalte-Informationen erleichtern das Auffinden von Problemen während des Parsens
  • Zahlen-Parsing wird nicht standardmäßig mitgeliefert; stattdessen können Nutzer Verfahren wie strtod, atoi oder andere gewünschte Methoden verwenden
  • String-Parsing kann ebenfalls direkt selbst implementiert werden, einschließlich Verarbeitung von Unicode-Surrogate-Pairs nach Bedarf
  • Der gesamte Quellcode wird als Public Domain (Unlicense) bereitgestellt und kann ohne Lizenzbeschränkungen verwendet oder geändert werden

Anwendungsbeispiel

  • Es wird ein einfaches Beispiel gezeigt, das einen JSON-String in eine Rect-Struktur parst
    char *json_text = "{ \"x\": 10, \"y\": 20, \"w\": 30, \"h\": 40 }";  
    typedef struct { int x, y, w, h; } Rect;  
    ...  
    
  • Verwendet eine einfache und klare API mit sj_Reader, sj_Value, sj_reader, sj_read, sj_iter_object usw.
  • Parsen numerischer Werte oder Vergleiche von Schlüssel-Wert-Paaren müssen selbst implementiert werden (keine builtin-Funktionen vorhanden)
  • Im Ordner demo finden sich verschiedene weitere Anwendungsbeispiele

Lizenz

  • Sj.h ist Public-Domain-Software, die jeder ohne Einschränkungen nutzen kann
  • Weitere Details stehen in der Datei LICENSE

Sonstiges

  • Durch den einfachen Code und die übersichtliche Ordnerstruktur lässt sich die Bibliothek ohne zusätzliche Konfiguration oder Build-Schritte direkt verwenden
  • Sie ist unabhängig, hat keine externen Abhängigkeiten und eignet sich vor allem für Umgebungen, in denen Leichtgewichtigkeit und einfache Nutzung wichtig sind

1 Kommentare

 
GN⁺ 2025-09-22
Hacker-News-Kommentare
  • Ich mag die Arbeiten dieses Autors, weil es meist Single-File-Bibliotheken sind, die in ANSI C oder Lua geschrieben wurden, sich auf einen klaren Umfang konzentrieren, eine leicht zu nutzende Schnittstelle und hervorragende Dokumentation bieten, und mir auch die Free-Software-Lizenz gefällt. Neben diesem Projekt gibt es noch einige weitere Arbeiten, die ich mag: log.c (eine einfache C99-Logging-Bibliothek), microui (eine winzige Immediate-Mode-UI-Bibliothek), fe (eine kleine einbettbare Sprache), microtar (eine leichtgewichtige tar-Bibliothek), cembed (ein Werkzeug zum Einbetten von Dateien in C-Header), ini (eine leichtgewichtige Bibliothek für .ini-Konfigurationsdateien), json.lua (eine leichtgewichtige JSON-Bibliothek für Lua), lite (ein leichtgewichtiger Texteditor in Lua), cmixer (ein portabler Audio-Mixer für Spiele), uuid4 (eine kleine uuid4-Bibliothek in C)
    • Ich hole mir für C-Projekte jedes Mal log.c dazu. Ich wusste gar nicht, dass der Autor so viele Bibliotheken gemacht hat. log.c ist wirklich leicht zu handhaben, daher kann ich es empfehlen.
    • Als ich mit LOVE2D Spiele gebaut habe, habe ich die Lume-Bibliothek verwendet. Ich habe den Autor tatsächlich ein paar Mal in einem IRC-Chatraum getroffen und einmal gesagt, eine seiner Ideen sei schlecht. Im Nachhinein betrachtet war es aber eine gute Idee. https://github.com/rxi/lume ist sehenswert.
    • Es ist zwar Open Source, aber keine echte Free Software.
  • Diese Bibliothek prüft in den folgenden Zeilen keinen signed integer overflow: L89, L149, L150. Bei bestimmten Eingabewerten kann es zu undefined behavior kommen.
    • Manche Entwickler mögen die Kultur einfacher Single-Header-C-Bibliotheken, ein Streamer wie Tsoding ist dafür ein typisches Beispiel. Man ist sich bewusst, dass solche Bibliotheken weder auf Sicherheit noch auf Funktionsfülle fokussiert sind. Nicht jedes Projekt ist Business-Grade-Software, die einer riesigen Zahl von Kunden ausgesetzt ist, daher ist dieser Ansatz auch in Ordnung.
    • Es gibt einen guten Artikel über den Ballast von Bibliotheken, die alle edge cases abdecken wollen, sowie eine zugehörige Diskussion. Wenn man versucht, jeden Fehler zu behandeln, steigt die Komplexität sprunghaft an. Manchmal ist das auch gar nicht die Verantwortung der Bibliothek.
    • UB kann auftreten, wenn die Verschachtelungstiefe über 2 Milliarden liegt oder im zweiten Fall die Zeilenzahl über 2 Milliarden hinausgeht. Wenn man JS-Eingaben auf 1 GB begrenzt, bekommt man bei größeren Eingaben sowieso an anderen Stellen des Stacks größere Probleme. Wenn ich JSON über 2 GB verarbeiten müsste, würde ich im Quellcode alle int auf 64 Bit umstellen. Aber auch dann stirbt es irgendwann, wenn die Eingabe größer als 2^64 ist. Ich habe nicht vor, im Code jeden einzelnen int-Overflow zu prüfen.
    • Da int 32 Bit hat, tritt das Problem nur auf, wenn man auf einer einzelnen Zeile eine Verschachtelungstiefe von 2 Milliarden hat, eine Datei mit mehr als 2 Milliarden Zeilen oder eine einzelne Zeile mit mehr als 2 Milliarden Zeichen.
    • Ich glaube nicht, dass sich diese Bibliothek für Production eignet.
  • Diese Bibliothek ist ziemlich tolerant. Ich würde nicht sagen, dass das schlecht ist, aber Leute, die sie verwenden, ohne den Code anzuschauen, sollten vorsichtig sein. Das ist der Hauptgrund, warum der Code so klein ist. Mit dem Demo-Beispiel aus dem README funktioniert sogar so etwas wie<br><code>{"x",10eee"y"22:5,{[:::,,}]"w"7"h"33<br>rect: { 10, 22, 7, 33 }</code>
    • Der Parser geht im Allgemeinen davon aus, dass die Eingabe gültig ist. Validierung ist ein völlig separates Problem, das diese Bibliothek nicht abdeckt. Letztlich extrahiert diese Bibliothek nur Daten.
    • Also ist das falsch?
  • Ich denke, JSON-Parser-Bibliotheken sind insgesamt ein schwarzes Loch des Schmerzes. Jede will irgendwo anders eingesetzt werden, und viele sind voller komplexer Abstraktionen, oft auch beides gleichzeitig. Tatsächlich ist es kein so schwieriges Problem, wenn man genau das implementiert, was man für den eigenen Zweck braucht.
    • Es ist erstaunlich, wie komplex moderne JSON-Bibliotheken werden. Die einst wirklich simple C++-Single-Header-JSON-Bibliothek von nlohmann ist<br>* schon 13 Jahre alt<br>* bekommt immer noch aktiv Pull Requests gemergt<br>* hat 122 Millionen Unit-Tests<br>und trotzdem sagen die Maintainer selbst, dass sie nicht die schnellste ist. Wenn man wirklich Geschwindigkeit braucht, empfehlen sie simdjson. Man sollte besser nicht auf die Idee kommen, selbst eine JSON-Parser-Bibliothek zu schreiben. Die ersten 90 % schafft man in 45 Minuten, aber für die restlichen 10 % brauchen zehntausend Leute tausende Stunden.
    • Es gibt das bekannte Material Parsing JSON is a Minefield (2016): https://seriot.ch/projects/parsing_json.html
    • So ein neutrales Design wie bei dieser Bibliothek ist wohl selten: Sie gibt einfach wiederholt Schlüssel und Array-Elemente zurück und unterscheidet Typen über String-Slices.
    • Dieses Projekt wirbt mit zero-allocation und minimalem State, aber für die am häufigsten genutzten Typen wie Strings braucht man am Ende doch Allokationen. Das ist eine ganz andere Alternative als das, was ich brauche.
    • Ich hoffe immer noch, dass S-Expressions beliebt bleiben.
  • Interessant, aber ich frage mich, wie viele Conformance-Tests diese Bibliothek besteht: https://github.com/nst/JSONTestSuite
    • Was Validierung angeht, scheint diese Bibliothek nicht besonders streng zu sein. Sie akzeptiert zum Beispiel sowohl ] als auch } zum Schließen von Objekten oder Arrays und behandelt auch \v als Whitespace, ist also toleranter als der Standard. Man sollte sie eher als „Datenextraktor für korrektes JSON“ sehen. Allerdings kann es lästig sein, selbst einen String- oder Number-Parser zu schreiben, besonders wenn man sich mit dem Produzenten auf ein Teilset der JSON-Syntax einigen muss.
    • Es wäre wirklich nützlich, Conformance-Tests auf Basis realer Implementierungen zu erstellen: also ein Test-Set, das exakt dem Teilset oder Superset entspricht, das sich wie eine bestimmte Parsing-Bibliothek verhält. So könnte man Risiken vermeiden, bei denen zwei Parser dasselbe JSON unterschiedlich interpretieren und dadurch Schwachstellen entstehen, etwa Auth-Bypass.
    • Ernstgemeinte Frage: Unterstützt das verschachtelte Objekte?
  • Wirkt ein bisschen wie ein halber Parser, weil er Zahlen nicht in float oder int umwandelt und man so etwas selbst ergänzen müsste. Trotzdem beeindruckend, und ich würde es wohl verwenden. Ich wollte nur darauf hinweisen.
  • Ich frage mich, ob C99 festlegt, dass diese Struktur standardmäßig mit 0 initialisiert wird, oder ob einfach = { 0 } fehlt. Im betreffenden Code könnte r->depth uninitialisiert sein und in der Schleife von sj__discard_until zufällig denselben Wert wie depth haben.
  • Ich frage mich, wofür man solche Bibliotheken überhaupt benutzt. Es scheint doch schon viele hervorragende JSON-Bibliotheken zu geben. Ist das ein Lehrmittel?
    • Sie lässt sich leicht in bestehende Codebasen integrieren, hat kaum Größen-Overhead, verwendet keine Heap-Allokation und nutzt nicht einmal die stdlib (es werden nur stdbool.h und stddef.h für Typdefinitionen eingebunden). Keine C++-Template-Spielereien, und die API ist direkt verständlich. C-Bibliotheken, die all diese Anforderungen erfüllen, sind wirklich selten, in C++ noch seltener.
    • Parsen ohne Overhead und Allokationen ist attraktiv. Wenn man aus großen JSON-Dumps, etwa Wikidata-Dumps, nur bestimmte Eigenschaften extrahieren will, ist das nützlich.
    • Man kann es direkt auf einer Embedded-CPU einsetzen. Inzwischen könnte man vielleicht sogar auf einer Vape so etwas wie einen API-Server laufen lassen.
    • Weil der Code klein ist, lässt er sich auch in sicherheitskritischen Projekten leichter prüfen, und auch die Lizenz-Compliance ist sehr einfach (kein Hinweistext erforderlich).
    • Für Einsteiger als Referenz oder für Leute, die etwas Einfaches parsen wollen, kann es eine gute Codebasis sein. In kleinen Hobbyprojekten mit begrenztem Prozessorbudget, wo ein kleiner Code-Footprint wichtig ist, ist es ebenfalls brauchbar. Wobei ich in solchen Fällen vermutlich eher ein Format wie TOML bevorzugen würde.
  • Cool! Wenn ich das nächste Mal einen JSON-Parser in C brauche, werde ich mir das anschauen. Ich habe über Jahre cJSON (https://github.com/DaveGamble/cJSON) gut genutzt. Davor habe ich wjelement (https://github.com/netmail-open/wjelement) verwendet, bin aber wegen einiger Probleme irgendwann umgestiegen (die genauen Gründe weiß ich nach so langer Zeit nicht mehr).
  • Es fühlt sich etwas erzwungen an, das einen Parser zu nennen. Im Allgemeinen wirkt es eher wie ein Lexer.