Library / SDK
json-parser/json-parser avatar
json-parser/json-parser

json-parser: a compact DOM parser for portable C89 projects

Very low footprint DOM-style JSON parser written in portable ANSI C

1,339 stars293 forksCBSD-2-Clause

At a glance

What is it?
json-parser builds an in-memory DOM from JSON using two dependency-free C source files. It suits small C or C++ programs that need predictable allocation and a direct node API, but it does not provide serialization.
Who is it for?
Adopt json-parser when your portable C or C++ program needs a compact, inspectable JSON DOM and can manage json_value lifetime through json_value_free. Do not adopt it expecting a built-in serializer or JSONPath-style query layer.
Can I use it commercially?
Yes. BSD-2-Clause 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 32 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 October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Why json-parser targets small portable C programs

json-parser addresses the need to parse JSON into inspectable values without introducing an external library dependency. It is aimed at developers working in portable C89, sometimes called ANSI C, as well as C++ code that can use its operator sugar. The repository presents the integration model directly: copy `json.c` and `json.h` into the project and compile them through the build system already in use. This is a narrower proposition than a complete JSON toolkit. The library parses JSON and exposes its structure, while serialization is delegated to the separate json-builder project.

The parsed representation is DOM-style. Each JSON value becomes a `json_value`, and its `type` field identifies an object, array, integer, double, string, boolean, or null. Object and array nodes retain references and lengths, so application code can traverse the parsed document through the public structure rather than through a streaming callback API. That model fits programs that need random access, recursive processing, or several passes over the same document.

The main design constraint is its footprint. According to the README, the parser never recurses and does not allocate more memory than it needs to represent the parsed JSON. Those are concrete implementation goals rather than benchmark results. The absence of recursion is particularly relevant to parsers exposed to untrusted input because recursive descent can consume stack proportional to nesting depth. This repository instead advertises a non-recursive parser. The documentation does not provide parser throughput, supported input limits, platform test results, or comparative memory measurements, so its practical footprint must be assessed in the target application rather than inferred from the project description.

The json_value tree and its typed fields

Parsing produces a tree rooted at a `json_value *`. The root's `type` determines which member of the `u` union is valid. For `json_object`, the README identifies `u.object.length`, `u.object.values[x].name`, and `u.object.values[x].value`. For `json_array`, the corresponding members are `u.array.length` and `u.array.values`. Scalar values use `u.integer`, `u.dbl`, `u.string.ptr`, `u.string.length`, `u.boolean`, or the `json_null` type.

This representation exposes JSON distinctions directly. Integers and doubles are separate node types, while strings retain a pointer and an explicit byte length rather than appearing as ordinary null-terminated C strings. Object values also have a `name_length` field according to the version 1.1.0 notes. That separation matters to C applications because embedded null bytes and numeric typing cannot be handled safely by reducing every JSON value to a text buffer.

The public parsing entry points are short:

c
json_value * json_parse (const json_char * json,
                         size_t length);

This function accepts the input buffer and its explicit length. The extended entry point accepts settings, the same buffer and length, and an error buffer:

c
json_value * json_parse_ex (json_settings * settings,
                            const json_char * json,
                            size_t length,
                            char * error);

The README does not document error codes, error buffer formatting, depth limits, or mutation of parsed nodes. It also does not present a traversal helper. Applications should therefore plan to validate the returned pointer and traverse values through the documented fields. Releasing a completed tree is handled by `json_value_free`:

c
json_value * json_parse (const json_char * json,
                         size_t length);
json_value_free (json_value *);

A practical program would store the returned root, inspect its `type`, read only the union member valid for that type, and pass the root to `json_value_free` when it is no longer needed. The README does not state whether `json_value_free` accepts NULL, so that should not be assumed without checking the header implementation.

Drop-in installation and a first parsing call

json-parser has no dependency installation step. The README says to add `json.c` and `json.h` to the project and compile them with the existing build system. A separate `Makefile.in`, `configure`, and related files are present, and the README states that the makefile can produce `libjsonparser` static and dynamic libraries. It does not provide the exact configure or make commands, so the repository does not support a more specific command sequence here.

At the source level, include `json.h` in application files, compile `json.c`, and provide any compile-time definitions consistently to both sides. A minimal first use consists of calling the parser with an input buffer and explicit length:

c
json_value * json_parse (const json_char * json,
                         size_t length);

The call returns a pointer to the root `json_value`. The caller should first check whether that pointer is NULL, then inspect `type` before accessing a member of `u`. When the tree is no longer required, call the documented cleanup function with the root pointer:

c
json_value * json_parse (const json_char * json,
                         size_t length);

json_value_free (json_value *);

No package manager command, required runtime, or external service is listed. The repository also contains `examples/test_json.c`, which is the documented starting point in the source tree for examining how the API is exercised. The README does not provide that file's contents, build arguments, or expected output, so this article does not invent them.

Compile-time configuration deserves special care because headers can be included in more than one translation unit. If `json_int_t`, `JSON_INT_MAX`, or `JSON_TRACK_SOURCE` is changed, the README says the definitions must be supplied both while compiling `json.c` and while compiling project files that include `json.h`. A mismatch can make the parser and its caller interpret the public structure differently.

Integer range, comments, allocation, and source locations

json-parser exposes several choices that affect portability and input policy. By default, `json_int_t` is `long` under C89 and `int_fast64_t` otherwise. MSVC uses `__int64` regardless of the documented language-standard setting. A developer can instead select another signed integer type because JSON unsigned integers are not supported. The README gives these examples exactly:

text
-Djson_int_t=short
"-Djson_int_t=signed char" -DJSON_INT_MAX=127
-Djson_int_t=long long
-Djson_int_t=__int128

A raw primitive type without `signed` or `unsigned` causes `JSON_INT_MAX` to be calculated. A typedef instead requires the application to define `JSON_INT_MAX` as the largest positive value that `json_int_t` can represent. This is a build-time compatibility decision, not merely an optimization. Selecting `short` or `signed char` reduces numeric range, while a type such as `long long` may not be portable across every compiler targeted under the project’s C89 requirement.

Comments can be enabled at runtime through the settings structure:

c
settings |= json_enable_comments;

When enabled, the parser accepts C-style `// line` and `/* block */` comments. Services that need strict JSON at an external boundary may prefer to leave this disabled, because such input is outside strict JSON syntax.

Applications can also supply `mem_alloc` and `mem_free` callbacks plus `user_data`. If those pointers are NULL, the library uses `malloc` and `free`. The `value_extra` setting reserves space after each `json_value` for application metadata. Defining `JSON_TRACK_SOURCE` adds source line and column information to each value, which can support application-level error reporting. The README does not specify the performance or memory cost of source tracking, comments, or extra storage.

What json-parser leaves outside its scope

The clearest boundary is serialization. The README explicitly directs users who want to serialize JSON to the separate json-builder project. json-parser concerns itself with parsing and exposing the resulting tree; it does not document a function for converting a `json_value` tree back into JSON.

It also does not present a schema validator, JSONPath engine, patch API, streaming parser, query language, or high-level wrapper for a particular standard library. Those features should not be inferred merely because the output has object and array nodes. A C application needing validation will have to enforce rules after parsing, while a program needing arbitrary path selection will have to navigate the public object and array fields itself.

The absence of recursion lowers one class of stack-consumption concern, but it does not make malformed input harmless. The project allocates a DOM and stores every parsed value, so deeply nested or very large documents can still consume substantial heap memory. The README does not publish maximum nesting depth, maximum document size, overflow policy, or behavior under allocation failure. It also does not state whether all documented JSON grammar edge cases are supported. Input validation at the application boundary remains relevant.

Custom allocation can help when an application needs accounting or a particular allocator. However, the documented callback signatures also make the allocation contract something the integration must understand:

c
void * (* mem_alloc) (size_t, int zero, void * user_data);
void (* mem_free) (void *, void * user_data);

The README says that `user_data` is forwarded from `json_settings`, but does not describe allocation failure behavior or callback reentrancy. Applications should not assume more than those documented contracts.

Maintenance, upgrades, and BSD-2-Clause implications

The repository is not archived, and its last push was on 2026-09-19. It has no GitHub releases, so there is no release entry that can be used as an upgrade baseline. The README includes a “Changes in version 1.1.0” section, which records UTF-8 byte order mark skipping, comment support, `name_length`, source tracking, `value_extra`, cross-compilation support through `--host`, an exposed maximum error-buffer size, and compiler-related fixes. The repository layout contains tests and an example, but the README does not explain how upgrades preserve source compatibility or how application metadata should be migrated between public structure changes.

The license is BSD-2-Clause. The project has no dependencies, which limits package and transitive dependency maintenance. Its source can be embedded directly in an existing build. That convenience does not eliminate upgrade work: maintainers still need to compare `json.h`, compile with the same type and source-tracking definitions, run project tests, and review changes to `json_value` fields or parsing behavior. The repository has no GitHub releases, so direct source integration should not be mistaken for a documented versioned package workflow.

The BSD-2-Clause identifier states the declared license, but this review does not provide legal advice. Review the repository’s `LICENSE` file for the exact obligations that apply to the intended distribution and modification pattern.

Editorial conclusion

Adopt json-parser when your portable C or C++ program needs a compact, inspectable JSON DOM and can manage json_value lifetime through json_value_free. Do not adopt it expecting a built-in serializer or JSONPath-style query layer. Before integration, verify integer range behavior on every target compiler, confirm that JSON_TRACK_SOURCE settings match across translation units, and decide whether accepting C-style comments is appropriate for the input boundary.

Frequently asked questions

Does json-parser require an external library or package manager?

No. The README says it has no dependencies and that `json.c` and `json.h` can be dropped into an existing project. A makefile can also produce static and dynamic libraries.

Does json-parser recurse when parsing nested JSON?

The README states that the parser never recurses. It still builds a DOM in allocated memory, so input size and nesting remain relevant to memory use.

Can json-parser serialize a parsed JSON value?

No serialization function is documented. The README directs serialization needs to the separate json-builder project.

Official sources

  1. Issues
  2. json-parser/json-parser on GitHub
  3. License: BSD-2-Clause
  4. README