Show HN: Parson, lighweight json parser in C
kgabis.github.com
kgabis.github.com
Well-written, clean and consistent. A bit too pendantic and therefore too verbose for my taste though :) Why in KnR's name this -
JSON_Object *new_obj = (JSON_Object*)parson_malloc(sizeof(JSON_Object));
is not written like this - JSON_Object *new_obj = parson_malloc(sizeof *newobj);
On a more serious note - I would move json_*_t structs to the header file and eliminate a bunch of API functions that service the access to these structs. It's a C library after all, everyone knows how to shoot one's own leg.(edit) Oh, and you don't handle realloc failures properly (at least in json_object_add).
Ayway, not that important... But there is a whole lot of people that like amalgated sources (typical examples - juce, freetype2, lua, sqlite) where you include in one or two files all the rest and they compile fine.
From what I know I would suggest to only do that if the code is never to be used as a shared library, as otherwise the structs can't be changed without recompiling all programs using the library.
There was a Debian security update to libxml2 in 2008 that made various Gnome apps including, IIRC, the window manager crash for many users in various circumstances (depending on the used theme etc.)(* ), because it changed some struct. Public API functions were provided in that case, but apparently direct access to structs was not discouraged enough to prevent some programs from going the unsafe route anyway (or perhaps the API wasn't sufficiently complete? I don't know the details).
I haven't created any wide spread C programs, and don't know if there are ABI checkers that Debian should use / have used, or if it's really just restraint to procedure based APIs that can avoid such problems.
(* ) e.g. http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=496125 http://lists.debian.org/debian-user/2008/08/msg01864.html
I strongly disagree. The author did exactly the right thing by hiding the internal data representation behind accessor functions. This makes it easy for him to change the internal data representation in the future without breaking binary compatibility.
See the libabc REAME for a fuller explanation: https://git.kernel.org/?p=linux/kernel/git/kay/libabc.git;a=...
Summary: "Do not expose any complex structures in your API"
Unrelated: please don't be one of those people who typedef every struct. It's really ugly and obfuscatory (see Linus' remarks on this).
["\ud83d\ude04"]json-parser, at first glance, appears to be nice, too. The ones I looked at before were all multiple files and seemingly very complicated--either to use or to build/integrate with my code. I'll keep a pointer to this one too for when I get back on that project.
I think in all fairness, I think the idea is that you should just drop it in your source without needing to worry about git subrepos (or whatever). But it would be perhaps more elegant to split it up in the repo and have a script stitch it together into the "only two files".
Also s7 turned out to be much easier to include than the other ones I was considering, so I guess I shouldn't be mocking it.
Main differences are that you use recursion instead of a stack, and that you dynamically resize your buffers as you parse (whereas json-parser passes through the JSON twice so that it knows exactly how much memory to allocate).
IMHO jsmn doesn't do nearly enough with the JSON to make it usable by an application (it's basically a lexer).
But you're right, if you want a json library to parse arbitrary json input using a more complex library like this one is the easier and more viable route than using jsmn.
Key differences:
* actual namespaces, no need to prefix everything with "JSON_"
* RAII-style resource management, no need for "json_value_free" etc
* objects, so instead of get_array(root_value) you have root_value.get_array()
You probably meant:
json::Array & commits = root_value.get_array();
Don't really want to copy an array there, right? After that you'll probably be tempted to use std::vector as a basis for json::array. Then you'll realize what an abysmal PoS default vector allocator is, and so you'd want to allow customizing it. Hello templates, and then before you know it, the whole thing not only looks like one of them incomprehensible boost beauties, but also compiles into a meg of code with a dozen dependencies. Neat :)Writing good code means having discipline to resist all sorts of temptations, such as the one you describe. And not all slippery slopes are actually all that slippery.
The one good thing about C++ is that you can pick the features you want and mostly ignore the rest. The reason C++ is so humungous is that everyone is using different subset of it.
I completely agree with this. Incidentally, this is exactly the reason why C++ libraries are far less useful than the C ones.
I don't see why you duplicated strndup in parson_strndup.
https://github.com/kgabis/parson/blob/master/parson.c#L129
parson_strdup can return NULL and this statement will fail silently.
The realloc check is good advice -- take heed, OP. :)
* Metaparadigm's "JSON-C" library (see https://github.com/json-c/json-c/wiki)
* Jansson (see http://www.digip.org/jansson/)
* yajl (see http://lloyd.github.com/yajl/)
All of those are licensed extremely permissively (BSD, ISC, etc) yajl seems kind of SAX-like, which I'm not really a big fan of, but I guess some people might like it. It would be good if you had a humungous JSON stream, for the same reasons SAX beats DOM in those cases.
json-c and Jansson seem pretty similar to me. Jansson seems to be more full-featured than json-c, with stuff like a json_equal method, a json_deep_copy method, and the ability to specify custom memory allocation functions. But for most use cases there isn't too much difference between the two.
So I guess my question is really what is the use case you're targeting? It would be nice to have some context about what you see as the niche you're filling.