Open-source project
DaveGamble/cJSON avatar
DaveGamble/cJSON

DaveGamble/cJSON: one C file that parses JSON and gets out of the way

Ultralightweight JSON parser in ANSI C

13,004 stars3,526 forksCMIT

At a glance

What is it?
The most widely vendored JSON parser in C, with an ANSI C89 constraint that shapes everything and a caveat list that is more useful than most library documentation.
Who is it for?
cJSON has survived this long because it makes one decision and refuses to revisit it: be the dumbest parser that gets the job done, and stay compilable as C89 so it drops into anything. Copying `cJSON.h` and `cJSON.c` into a tree still works, and the CMake path with its ten build options is there when you want the tests, sanitizers or the utils library.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 20 days ago.
What is it written in?
Mainly C, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 21, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Copying two files into your source tree

cJSON is two files, `cJSON.h` and `cJSON.c`, and the README's first build option is to copy them into your project and start using them. There is no configure step, no code generation and no runtime dependency beyond the C standard library. For an embedded target or a firmware project, that is the whole integration story.

The reason it can be that simple is the language level. cJSON is written in ANSI C, C89 specifically, chosen to support as many platforms and compilers as possible. There is a second optional file, `cJSON_Utils.c` with `cJSON_Utils.h`, which is a separate helper library rather than part of the core.

The scale of this is worth stating. At 13004 stars and 3526 forks, this is one of the most depended on C libraries in existence, and it is almost entirely through vendoring rather than through a package manager. The repository is not archived and the last push was 2026-09-16. The MIT licence is why that pattern is possible: no copyleft obligations, no attribution ceremony beyond keeping the notice.

CMake as the supported build, Makefile explicitly deprecated

The README is unusually direct about which build path to use. The Makefile section opens with a note that the method is deprecated and that support is limited to fixing bugs. CMake gets a full blown build system, and the recommended shape is an out of tree build:

bash
mkdir build
cd build
cmake ..

That produces a Makefile you then drive normally:

bash
make

Installation defaults to `/usr/local/include/cjson` and `/usr/local/lib`, and it also installs pkg-config files and CMake config files so other CMake projects can discover the library without manual path configuration.

The packaging example in the README is the most useful part for anyone shipping cJSON in a Linux distribution, because it turns off what a distro build should not carry:

bash
cmake .. -DENABLE_CJSON_UTILS=On -DENABLE_CJSON_TEST=Off -DCMAKE_INSTALL_PREFIX=/usr

The `ENABLE_CJSON_TEST` and `ENABLE_CJSON_UTILS` switches default to on and off respectively, which is the opposite of what you want for a package: you want the utils library and you do not want the test program. `ENABLE_TARGET_EXPORT` exists so you can turn CMake target export off if it causes problems downstream, and `CJSON_OVERRIDE_BUILD_SHARED_LIBS` exists because build systems that set `BUILD_SHARED_LIBS` globally would otherwise leak their preference into your build.

Build options for the sanitizers most C projects skip

The CMake options list is where this project shows its age and its care at the same time. Two options exist purely for finding memory bugs: `ENABLE_VALGRIND` runs the tests under valgrind, and `ENABLE_SANITIZERS` compiles cJSON with AddressSanitizer and UndefinedBehaviorSanitizer enabled where the compiler supports it. A third, `ENABLE_SAFE_STACK`, adds the Clang SafeStack instrumentation pass for stack overflow resistance, and the README notes it currently only works with Clang.

`ENABLE_LOCALES` is the one that surprises people, because it is on by default. It controls whether the library uses `localeconv`, which affects how numbers are parsed and printed. Turning it off gives you locale independent behaviour, which is what you want in a program whose output is consumed by another program rather than read by a person.

The two settings for compilers are `ENABLE_CUSTOM_COMPILER_FLAGS`, on by default, and `ENABLE_CJSON_VERSION_SO`, also on by default, which exposes the version through the shared object. Everything is a plain On or Off flag, which is a small courtesy that makes the option list greppable.

The Makefile in the repository shows how strict the project is with the compiler itself. It builds with `gcc -std=c89` and a warning set including `-Wall -Werror -pedantic -Wstrict-prototypes -Wwrite-strings -Wshadow -Winit-self -Wcast-align -Wformat=2 -Wmissing-prototypes -Wcast-qual -Wc++-compat -Wundef -Wswitch-default -Wconversion`. That is not a casual warning list, and `-Wconversion` in particular catches the class of bug where an integer width silently changes meaning.

The caveat list is the part worth reading twice

The README has a table of contents, and under Caveats it lists eight topics. That list is a better summary of what cJSON is than any feature list would be: the zero character, character encoding, the C standard, floating point numbers, deep nesting of arrays and objects, thread safety, case sensitivity, and duplicate object members.

Each one is a real limitation rather than a hypothetical. The zero character section is about strings that contain embedded nulls, which a JSON parser representing strings as C strings cannot represent. The deep nesting section is about recursion: a parser that recurses per nesting level will exhaust the stack on a document nested far enough, and version 1.7.19 added a maximum recursion depth to `cJSON_Duplicate` for exactly that reason. Thread safety means what it says: separate parse trees are independent, but sharing one tree across threads needs external locking.

Case sensitivity and duplicate object members are a matched pair. JSON member names are case sensitive, so `Name` and `name` are two different members. Duplicate members in one object are accepted by the parser rather than rejected, which is lenient in a way that can surprise you if you assume the last one wins.

This is the part of the documentation that makes cJSON credible. A library that lists what it cannot do, in its own README, next to the build instructions, is a library written by someone who has been asked why it broke.

Three releases, two CVEs and a hardening trend

The three tagged releases tell a consistent story: maintenance releases built from reported crashes, with security fixes when they turn out to be reachable.

Version 1.7.19, published 2025-09-09, is a batch of NULL checks and bounds fixes. It checks for NULL in `cJSON_DetachItemViaPointer`, checks for pointer overlap before calling `strcpy` in `cJSON_SetValuestring`, adds a maximum recursion depth to `cJSON_Duplicate` to prevent stack exhaustion, allocates memory for the temporary buffer when parsing numbers, and fixes an incorrect check in `decode_array_index_from_pointer`. It also fixes indentation to use spaces and cleans up spelling errors caught by CodeSpell.

Version 1.7.18, from 2024-05-13, is the security release. It adds a NULL check to `cJSON_SetValuestring` for CVE-2024-31755 and fixes a heap buffer overflow. It also does the unglamorous work: removing non-functional compiler flag handling, removing a misused optimisation flag that looked like `-O1` typed as `-01`, and setting freed pointers to NULL whenever they are not immediately reassigned.

Version 1.7.17, from 2023-12-26, fixed two null dereferences, CVE-2023-50471 in `cJSON_InsertItemInArray` and CVE-2023-50472 in `cJSON_SetValuestring`.

The Makefile pins `LIBVERSION = 1.7.19`, so the repository's own build and its newest release agree. If you are vendoring an old copy, that mismatch is the first thing to check.

How this compares to the alternatives in C

There is no shortage of JSON options in C, and the interesting comparison is with the two other common choices rather than with any modern language.

Against jsmn, which is a tokenizer rather than a parser, cJSON builds a tree you own and must free. jsmn produces callbacks over your own buffer and costs almost nothing, but you write all the tree handling yourself. For a fixed schema in embedded firmware, jsmn is the smaller hammer; for a document whose shape you do not fully control, the tree is worth the allocation.

Against a generated deserializer such as something from a protobuf or flatbuffers toolchain, cJSON is the opposite trade. Generated code gives you a struct and a fixed schema with no parsing cost at runtime, which is faster and safer, but only works if you control both ends. cJSON works when the JSON arrives from somewhere you do not control.

The honest ceiling is that cJSON is a tree parser with no streaming mode. A document that arrives as an endless stream has to be buffered whole, which defeats the point of streaming in the first place. And because the parser is recursive and takes no depth limit on the parse path itself, hostile input nesting is a real consideration for anything exposed to untrusted JSON. The `ENABLE_SANITIZERS` option exists for a reason.

The tests directory, `test.c`, `tests/` and the `fuzzing/` directory are the parts of the repository that tell you the most about how it is maintained. The presence of a fuzzing directory next to the sanitizer build option is the honest signal about how a C parser earns trust.

Editorial conclusion

cJSON has survived this long because it makes one decision and refuses to revisit it: be the dumbest parser that gets the job done, and stay compilable as C89 so it drops into anything. Copying `cJSON.h` and `cJSON.c` into a tree still works, and the CMake path with its ten build options is there when you want the tests, sanitizers or the utils library. The thing to read before using it is the caveat list, because the limits are documented rather than hidden: embedded null characters in strings, encoding assumptions, floating point behaviour, recursion depth on deep nesting, and the fact that the library is not thread safe. Where cJSON is a poor fit is anywhere the JSON is adversarial or the volume is large, since there is no streaming parser and no schema. Pin a release, read the CHANGELOG for the CVE fixes, and turn on the sanitizer build option while you are developing.

Frequently asked questions

Is there a library for handling JSON data in C?

cJSON is the usual answer. It is a single C file and header written in ANSI C89, licensed under MIT, and designed to be copied into a project rather than linked as a dependency. The README covers CMake, a deprecated Makefile path, Meson, vcpkg, and plain source copying, and lists the limitations honestly under a caveats section covering encoding, nesting depth and thread safety.

Is cJSON thread safe?

Not for a shared tree. Separate cJSON items parsed into separate trees do not interfere with each other, but reading or modifying one item from multiple threads needs external locking. Thread safety is one of the eight topics listed in the README caveats section, alongside the zero character, encoding, the C standard, floating point numbers, nesting depth, case sensitivity and duplicate object members.

How do I build cJSON for a Linux distribution package?

Use an out of tree CMake build and turn off the test target while turning on the utils library. The README gives the pattern as `cmake .. -DENABLE_CJSON_UTILS=On -DENABLE_CJSON_TEST=Off -DCMAKE_INSTALL_PREFIX=/usr`, then `make` and `make DESTDIR=$pkgdir install`. The Makefile build path is explicitly deprecated and kept only for bug fixes.

Which cJSON version should I use?

Version 1.7.19 is the newest tag and matches the `LIBVERSION` in the repository Makefile. It contains fixes for stack exhaustion in `cJSON_Duplicate`, a pointer overlap check before `strcpy` in `cJSON_SetValuestring`, and a temporary buffer allocation when parsing numbers. Earlier versions 1.7.17 and 1.7.18 addressed CVE-2023-50471, CVE-2023-50472 and CVE-2024-31755.

Official sources

  1. DaveGamble/cJSON on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/davegamble-cjson.svg)](https://hysenlabs.com/projects/davegamble-cjson)