17 ms·
Sj.h: A tiny little JSON parsing library in ~150 lines of C99
- fnord77 1y agoI can see one bug just glancing at the code - feeding a stray '}' at the top level can result in depth becoming negative
- flohofwoe 1y agoThat's detected as an error though? https://github.com/rxi/sj.h/blob/eb725e0858877e86932128836c1f4e4f253cf8e4/sj.h#L95-L98 https://github.com/rxi/sj.h/blob/eb725e0858877e86932128836c1...
- EE84M3i 1y agoThis is interesting, but how does this do on the conformance tests? https://github.com/nst/JSONTestSuite https://github.com/nst/JSONTestSuite
- Lucas_Marchetti 1y agoReal question, does it manage nested objects ?
- morcus 1y agoIt seems so: https://github.com/rxi/sj.h/blob/master/demo%2Fobject.c https://github.com/rxi/sj.h/blob/master/demo%2Fobject.c
- Lucas_Marchetti 1y agoyep but how deep can you parse nested into nested etc
- layer8 1y agoWhy don’t you look at the source code, it’s only 150 lines? The nesting is limited by using an int as the depth counter. The C standard guarantees that MAX_INT is at least 32767, so that’s a limit on portable nesting depth. Nowadays int is typically 32 or 64 bits, so a much higher limit in typical C implementations. If I see correctly, the library doesn’t check for overflow, however. This might conceivably be an exploitable vulnerability (and such an overflow would constitute UB).
- johnisgood 1y agoEasy to add such checks though.
- catlifeonmars 1y agoYou know what would really be useful is a conformance test based on a particular real implementation. What I mean by this is a subset (superset?) that exactly matches the parsing behavior of a specific target parsing library. Why is this useful? To avoid the class of vulnerabilities that rely on the same JSON being handled differently by two different parsers (you can exploit this to get around an authorization layer, for example).
- LegionMammal978 1y agoIt doesn't seem to have much in the way of validation, e.g., it will indiscriminately let you use either ']' or '}' to terminate an object or array. Also, it's more lenient than RFC or json.org JSON in allowing '\v' for whitespace. I'd treat it more as a "data extractor for known-correct JSON". But even then, rolling your own string or number parser could get annoying, unless the producer agrees on a subset of JSON syntax.
- adrianN 1y agoWhat’s the usecase for something like this? There are lots of excellent libraries for json available. Is this a teaching tool?
- p2detar 1y ago> Zero-allocations with minimal state
- Snild 1y agoSmall code is easier to review, so projects with strict security requirements might be one? Also, license compliance is very easy (no notice required).
- adsan 1y agoI suppose it's meant as a minimal library meant to be modded for the specific usecase.
- bb88 1y agoEmbedded cpus is an easy one. You could maybe run an api server on a vape now.
- flohofwoe 1y agoTrivial to integrate into an existing code base, minimal size overhead, no heap allocations, no stdlib usage (only stdbool.h and stddef.h included for type definitions), no C++ template shenanigans and very simple and straightforward API. C libraries which tick all those boxes are actually quite rare, and C++ libraries are much rarer.
- CyberDildonics 1y agoA small single file, pure C dependency that doesn't allocate memory can be a universal solution to a common problem if it works well.
- elcapitan 1y agoBeing able to parse without a lot of overhead and without allocations is quite interesting. E.g. when you process some massive json dump to just extract some properties (the Wikidata dumps come to mind).
- lioeters 1y agoWhat I love about this author's work is that they're usually single-file libraries in ANSI C or Lua with focused scope, easy-to-use interface, and good documentation. And free software license. Aside from the posted project, some I like are: - log.c - A simple logging library implemented in C99 - microui - A tiny immediate-mode UI library - fe - A tiny, embeddable language implemented in ANSI C - microtar - A lightweight tar library written in ANSI C - cembed - A small utility for embedding files in a C header - ini - A tiny ANSI C library for loading .ini config files - json.lua - A lightweight JSON library for Lua - lite - A lightweight text editor written in Lua - cmixer - Portable ANSI C audio mixer for games - uuid4 - A tiny C library for generating uuid4 strings
- maldonad0 1y agoIt's open source, not free software.
- retired_account 1y agoThe license says otherwise; hard to get freer than public domain.
- tripplyons 1y agoOpen source is a more informative term for this than free software. Not all free software is open source, but all open source software is free. Edit: I was not aware of the FSF's definition. I was using a definition of free software being software that you can use without having to pay for it.
- manbash 1y ago> Not all free software is open sourc Depends on which "free software" definition you're referring to. The FSF definition of "free software" requires it to be open source.
- 1y ago
- codr7 1y agoJSON parser libraries in general is a black hole of suffering imo. They're either written with a different use case in mind, or a complex mess of abstractions; often both. It's not a very difficult problem to solve if you only write exactly what you need for your specific use case.
- flohofwoe 1y agoYou can't get much more 'opinion-less' than this library though. Iterate over keys and array items, identify the value type and return string-slices.
- IshKebab 1y agoIt also feels like only half the job to me. Reminds me of SAX "parsers" that were barely more than lexers.
- flohofwoe 1y agoI mean, what else is there to do when iterating over a JSON file? Delegating number parsing and UNICODE handling to the user can be considered a feature (since I can decide on my own how expensive/robust I want this to be).
- skydhash 1y agoThat is what I like Common Lisp libraries. They are mostly about the algorithms, leaving data structures up to the user. So you make sure you got those rights before calling the function.
- IshKebab 1y agoExtracting the data into objects. Libraries like Serde and Pydantic do this for you. Hell the original eval() JSON loading method did that too.
- meindnoch 1y ago
- layer8 1y agoThe library doesn’t check for signed integer overflow here: https://github.com/rxi/sj.h/blob/eb725e0858877e86932128836c1f4e4f253cf8e4/sj.h#L89 https://github.com/rxi/sj.h/blob/eb725e0858877e86932128836c1... https://github.com/rxi/sj.h/blob/eb725e0858877e86932128836c1f4e4f253cf8e4/sj.h#L149 https://github.com/rxi/sj.h/blob/eb725e0858877e86932128836c1... https://github.com/rxi/sj.h/blob/eb725e0858877e86932128836c1f4e4f253cf8e4/sj.h#L150 https://github.com/rxi/sj.h/blob/eb725e0858877e86932128836c1... Certain inputs can therefore trigger UB.
- EmilStenstrom 1y agoSubmit a PR!
- uncircle 1y agoYou don't get Hacker News karma if you quietly fix a bug instead of complaining about it, though.
- skydhash 1y agoThere was a nice article [0] about bloated edge cases libraries (discussion [1]). Sometimes, it's just not the responsibility of the library. Trying to handle every possible errors is a quick way to complexity. [0]: https://43081j.com/2025/09/bloat-of-edge-case-libraries https://43081j.com/2025/09/bloat-of-edge-case-libraries [1]: https://news.ycombinator.com/item?id=45319399 https://news.ycombinator.com/item?id=45319399
- layer8 1y agoThe problem in the present case is that the caller is not made aware of the limitation, so can’t be expected to prevent passing unsupported input, and has no way to handle the overflow case after the fact.
- skydhash 1y agoDo you not review libraries you add to your project? A quick scan of the issues page if it's on a forge? Or just reading through the code if it's small enough (or select functions)? Code is the ultimate specification. I don't trust the docs if the behavior is different from what it's saying (or more often fails to mention). And anything that deals with recursive structures (or looping without a clear counter and checks) is my one of the first candidate for checks. > has no way to handle the overflow case after the fact. Fork/Vendor the code and add your assertions.
- sim7c00 1y agothis is really nice. i also _must_ use it because my initials are S.J H.. :'). on the more code side, love this, been looking to implement a simple json parser for some projects but this is small enough i can study it and either learn what i need or even use it. lovely!
- cindyllm 1y ago[dead]
- mbel 1y agoIt feels like a stretch to call this a parser. It’s looks like a typical lexer?
- alexey-salmin 1y agoWouldn't be possible with yaml
- dustbunny 1y agoIt looks like it's kinda half a parser in that it doesn't turn numbers into floats or ints, so you'd have to build that on top of it. Im still impressed and might use it, but just noting this.
- mcflubbins 1y agoNeat! Might look at this next time I need a JSON parser in C. I've been using cJSON[0] for years now and am pretty happy with it. I had used wjelement[1] before that but ran into a few issues and eventually moved away from it (can't recall why exactly its been so long.) [0] https://github.com/DaveGamble/cJSON https://github.com/DaveGamble/cJSON [1] https://github.com/netmail-open/wjelement https://github.com/netmail-open/wjelement
- commandersaki 1y agoAh a Tsoding special, I'll have to find the livestream where he implemented it, and with his funny laugh of "ohohohoho".
- jeroenhd 1y agoI really enjoy these simple libraries, even though they are too flawed to be used for anything serious. There's great freedom in just drilling down to the basics, ignoring all the complexities, and just writing code that'll probably work most of the time.
- LiamPowell 1y agoThis is rather lenient. There's not anything wrong with that (although perhaps it should be noted for people that will use it without looking at the code), but it's the main reason this can be so small. Using their demo in the readme: {"x",10eee"y"22:5,{[:::,,}]"w"7"h"33 rect: { 10, 22, 7, 33 }
- hackernewds 1y agoso it is wrong?
- hacker_homie 1y agoParser, implies the input is assumed to be valid, validating is a whole other problem not covered by this library. I don’t know what else you call a library that just extracts data.
- andrewmcwatters 1y agoA recommendation: we use rxi's json.lua at Planimeter, but upon benchmarking JSON implementations across all major languages, it was found to be the slowest by an order of magnitude when reading very large numbers of JSON files. So if you can, try and at least use LuaJIT, which when using json.lua seems to bring it back down into range with other performant languages, or jump down into LuaJIT and use Sj.h there, through the C FFI or just simdjson. json.lua is great for when you're restricted in some ways to use a pure Lua implementation, though. It's the de facto solution.
- andrewmcwatters 1y agoOther JSON libraries benchmarked here: https://github.com/andrewmcwattersandco/programming-language-benchmarks https://github.com/andrewmcwattersandco/programming-language...
- fennec-posix 1y agoI see this being useful for embedded/micro-controller use, very nice!
- slashdave 1y agoI believe json requires unicode...
- flohofwoe 1y agoAs far as I can see, the library is encoding agnostic (eg ASCII, Extended ASCII and UTF-8 should all work fine - and UTF-8 is the defacto standard UNICODE encoding since the late 1990s.
- z0r 1y agoDoes C99 specify that this structure would be 0 initialized by default, or is this line missing a = { 0 }? - https://github.com/rxi/sj.h/blob/5cb5df45c8c37fd8c2322026a111603e2adfa675/sj.h#L119 https://github.com/rxi/sj.h/blob/5cb5df45c8c37fd8c2322026a11... - it reads to me like r->depth might be randomly initialized and equal to depth by random chance on the first iteration of the sj__discard_until loop
- oguz-ismail 1y agor->depth would have been initialized to zero here https://github.com/rxi/sj.h/blob/5cb5df45c8c37fd8c2322026a111603e2adfa675/sj.h#L36 https://github.com/rxi/sj.h/blob/5cb5df45c8c37fd8c2322026a11...
- z0r 1y agothanks, i completely misread this
- lelanthran 1y agoThis is quite neat. I wrote a similar library for no-alloc JSON parsing, but never had use for it. This does actual parsing, though; my approach is to just navigate through a JSON tree. https://github.com/lelanthran/libxcgi/blob/master/library/src/xcgi_json.h https://github.com/lelanthran/libxcgi/blob/master/library/sr... https://github.com/lelanthran/libxcgi/blob/master/library/src/xcgi_json.c https://github.com/lelanthran/libxcgi/blob/master/library/sr...
- rurban 1y agoToo simple, I prefer jsmn for that, https://github.com/zserge/jsmn https://github.com/zserge/jsmn Which does much more in 200 lines of C89 in a single header
- oguz-ismail 1y agoIt's more than 400 lines
- queuebert 1y agoWhat is the purpose of declaring an sj_Reader object with an sj_read method if you don't support lazy parsing? Seems like you could tighten up the code by combining the declaration and parsing into one subroutine.
- teo_zero 1y agoTotally agree! I've never understood the beauty of singleton objects.
- ndnxnxb 1y agoWith JSON being so simple, how do you even manage to create a library which accepts so many invalid documents