|
json-gen-c
0.1.5
json-gen-c generate C code for json manipulation
|
Fast, tiny, and friendly code generator that turns your C structs into fully featured JSON serializers/deserializers.
sstr string helper library and ready-made array helpers.
json-gen-c is faster than cJSON on all benchmarks — up to 47% faster marshal, 25% faster unmarshal — while providing type-safe code generation, selective parsing, and multi-format support (JSON + MessagePack + CBOR). See benchmark/RESULTS.md for full results and reproduction steps.
json-gen-c is a program for serializing C structs to JSON and deserializing JSON to C structs. It parses struct definition files then generates C code to handle both directions.

The project uses a modern, efficient build system with support for parallel compilation:
To build example, tests, and benchmarks
make benchmark-repro installs the distro benchmark dependencies when needed, clones yyjson into benchmark/yyjson/, installs it locally under benchmark/.deps/prefix/, then builds and runs the full benchmark suite with -O2 -DNDEBUG. Both local dependency directories are gitignored on purpose.
All build artifacts are organized under the build/ directory:
build/bin/ - Main executablebuild/lib/ - Static libraries build/example/ - Example executablebuild/test/ - Test executablesbuild/benchmark/ - Benchmark executableCMake (3.16+):
Windows (CMake + MSVC):
Windows (CMake + MinGW):
Note: On Windows, CMake automatically uses a built-in
xxdreplacement (cmake/xxd.cmake). No external tools beyond a C compiler and CMake are needed.
Meson:
See BUILD_SYSTEM.md for pkg-config integration, find_package(JsonGenC), and the json_gen_c_generate() CMake macro.
Homebrew (macOS/Linux):
To set up the tap, see packaging/homebrew/.
Debian/Ubuntu (from source package):
Arch Linux (AUR):
For example, create a file name struct.json-gen-c as contents below:
Note that we don't use C-style string char*, a more resonable type is sstr_t. You can find more details about sstr_t in document of sstr.
This generates the following files in your specified destination directory:
json.gen.h, the header which declares your generated structures and functions.json.gen.c, which contains the implementation of your functions.sstr.h, sstr.c, the string manipulation helper functions that generated code depends on.To generate MessagePack (binary) serialization instead of JSON:
This generates msgpack.gen.h and msgpack.gen.c with msgpack_pack_* / msgpack_unpack_* functions. The struct definitions and sstr helper are identical regardless of format.
To generate CBOR (RFC 8949) binary serialization:
This generates cbor.gen.h and cbor.gen.c with cbor_pack_* / cbor_unpack_* functions. Same struct definitions and API patterns as JSON and MessagePack.
This generates json_gen_c.gen.hpp — a C++17 header with RAII wrapper classes inside namespace jgc. Each class wraps a generated C struct with:
_init), destructor (_clear), move and copy semantics backed by the generated C copy/move helpers for JSON outputstd::string, enums as their C enum type)marshal(), unmarshal(), and unmarshal_into() member functionsc_struct() for C interopCan be combined with --format to also generate MessagePack or CBOR alongside.
This generates json_gen_c.gen.rs — a self-contained Rust module with native serde-compatible structs and enums. Add serde and serde_json to your Cargo.toml, then:
Type mapping: int→i32, long→i64, float→f32, double→f64, sstr_t→String, enums→Rust enums, oneof→#[serde(tag)] enums, optional→Option<T>, arrays→Vec<T>/[T; N], maps→HashMap<String, V>.
This generates json_gen_c.gen.go — a self-contained Go source file using encoding/json struct tags. No external dependencies required:
Type mapping: int→int32, long→int64, float→float32, double→float64, sstr_t→string, enums→type E string, oneof→custom marshal/unmarshal, optional→*T + omitempty, arrays→[]T/[N]T, maps→map[string]V.
Every generated struct and oneof type also has deep-copy and ownership-transfer helpers:
<type>_copy(dest, src) clears dest before performing a full deep copy. Strings, arrays, maps, nested structs, oneof payloads, and optional/nullable presence flags are duplicated so the result does not share owned storage with src. If allocation fails, it returns -1 and leaves dest in a clearable state; the previous dest value is not preserved.
<type>_move(dest, src) clears dest, transfers ownership from src, then leaves src in a clearable moved-from state without reapplying schema defaults. Self-copy and self-move return 0 without changing the object.
Selective unmarshal only updates the chosen top-level fields that are present in the JSON input. Unselected top-level fields stay unchanged.
Selected fields are treated as whole-field replacements: if a selected field is a string, array, map, nested struct, or oneof, its previous stored value is cleared before the new JSON value is parsed.
Use the _deep variant to pass sub-field masks into nested structs:
When nested_masks is NULL or no entry matches a field, the nested struct is parsed in full (same as the non-deep API). Sub-masks can be chained recursively via the sub_masks / sub_mask_count members for deeper nesting levels.
Field-mask constants use the generated C field names, even when @json aliases change the JSON key names.
For detailed build system documentation, see BUILD_SYSTEM.md. If you are new to the project, the friendly walkthrough in docs/GETTING_STARTED.md covers installation, schema authoring, and integration.
Define a struct like:
The field type can be one of the following:
intlongfloatdoublesstr_tboolmap<sstr_t, V> where V is any of the above typesIf a field is a dynamic array, just append [] after the field name. For fixed-size arrays, use [N] where N is a positive integer (e.g., int data[10];).
Map fields marshal to/from JSON objects. The key type is always sstr_t (JSON keys are strings). Example:
In generated C code, each map is represented as a dynamic array of key-value entries:
Tagged unions (discriminated unions) represent a value that can be one of several types, identified by a discriminator tag field in JSON:
@tag "field" sets the JSON discriminator key (defaults to "type" if omitted).{"type":"circle","radius":5.0}.Mark fields, enum values, or oneof variants as deprecated:
Deprecated items remain fully functional in marshal/unmarshal. The generated C code annotates them with compiler deprecation attributes so downstream code that accesses them gets a warning. See doc/schema-evolution.md for migration patterns and compatibility rules.
Oneof types can be used as fields in structs:
Use the generated helper macros to manage mask bits:
For json_unmarshal_selected_<struct_name>() and _deep():
NULL or too few mask words returns an error_deep additionally accepts json_nested_mask entries for sub-field selection within nested structsThe editors/vscode/ directory contains a VS Code extension providing:
.json-gen-c files (keywords, types, annotations, etc.)To enable the language server, ensure json-gen-c is on your PATH (or set jsonGenC.serverPath in VS Code settings), then install the extension. It launches json-gen-c --lsp automatically.
Any LSP-capable editor can use the built-in language server:
This runs an LSP server over stdin/stdout (JSON-RPC 2.0). Configure your editor to launch it as a language server for .json-gen-c files.
@deprecated).We welcome issues, ideas, documentation updates, and code contributions.
Thanks for helping json-gen-c grow. Happy hacking!
Codes of json-gen-c are licensed under GPL-3.0, except for the codes it generated. The copy right of the codes generated by json-gen-c is owned by the user who wrote the struct definition file, same as the copy right of a PDF file generated by Latex is owned by the user who wrote the tex file.