What is JSON to C Struct Generator?
C has no built-in JSON support and no garbage collector, so consuming a JSON API in C means two separate decisions: what the struct layout should look like, and who owns the memory behind every string and array. This generator handles the first decision and gives you a head start on the second. It infers a C type for each JSON value, converts keys to snake_case (the dominant C convention), emits child structs before parents so the file compiles in a single pass, and lets you choose between a static layout (fixed char buffers, no malloc) and a dynamic layout (char* with generated create/free helpers). The key thing C struct generation has to confront that managed languages do not: C strings are char* or char[N], and there is no String type that knows its own length. A JSON string of unbounded length cannot safely go into char[256] — it will truncate or overflow. So the static/dynamic choice is not cosmetic, it changes the safety profile of your code. Likewise JSON arrays have no fixed size, so each array becomes a pointer-plus-count pair (the canonical C idiom) rather than a single field. Most real C projects do not hand-roll a JSON parser; they link cJSON or jansson. This tool's output is designed to drop into either: enable library stubs and you get from_json/to_json skeletons that walk the parsed object tree.
How to Use
- Set the root struct name (e.g. ApiResponse) and an optional prefix to namespace nested struct names, since C has no modules
- Choose static memory mode for embedded or stack-allocated data where you control the size, or dynamic mode when string lengths are unbounded and you will malloc/free
- Enable cJSON or jansson stubs to get from_json()/to_json() skeletons instead of writing the object-walking boilerplate by hand
- Paste your JSON and click Generate, then copy the typedef block into a .h file and the function stubs into the matching .c file
- In dynamic mode, call the generated free helper for every struct you create — C will not reclaim the heap memory for you
Why Use This Tool?
Tips & Best Practices
- Never parse JSON in C by hand for production code — link cJSON (single-file, MIT) for small projects or jansson (refcounted, more features) for larger ones. Hand-written parsers miss escape sequences and UTF-8 edge cases.
- In static mode, char[256] silently truncates longer strings. If you cannot bound the length, use dynamic mode or you will introduce data corruption that only shows up with real-world input.
- C89 has no bool — the generator maps JSON booleans to int (0/1). If you target C99 or later, change these to bool and #include <stdbool.h> for clearer code.
- For nullable nested objects, dynamic mode uses a pointer that is NULL when absent. Always NULL-check before dereferencing — a missing JSON key is the number-one cause of segfaults in JSON-consuming C code.
- cJSON_Parse returns a tree you must free with cJSON_Delete, separately from freeing your own struct. Mixing up these two ownership chains causes either leaks or double-frees.
Frequently Asked Questions
What is the complete JSON type to C type mapping?
JSON string → char* (dynamic mode) or char[256] (static mode); JSON integer → int (use long or int64_t for values beyond 2^31); JSON float → double; JSON boolean → int holding 0 or 1 (or bool with stdbool.h in C99+); JSON null → a NULL pointer in dynamic mode or a sentinel value in static mode; JSON array → a pointer to the element type plus a size_t count field; JSON object → a separate nested typedef struct referenced by value or pointer.
Should I write my own JSON parser or use a library like cJSON or jansson?
Use a library. cJSON is a single header/source pair under the MIT license, ideal for embedding in small or embedded projects. jansson uses reference counting and offers richer manipulation and a streaming API, better for larger applications. A hand-written parser has to correctly handle string escapes (\u sequences, surrogate pairs), number formats, nested depth limits, and UTF-8 — getting all of that right is far more work than linking a proven library, and bugs there are security-relevant.
What is the difference between static and dynamic memory mode?
Static mode uses fixed-size buffers (char[256]) and fixed-capacity arrays, so there is no malloc and no cleanup, but strings longer than the buffer truncate. Dynamic mode uses char* fields and pointer arrays sized at runtime, generating create() and free() helpers that call malloc/free. Use static for bounded, embedded, or stack-allocated data; use dynamic whenever string or array sizes come from untrusted external input.
How do I manage memory for nested structs and arrays?
In dynamic mode each generated free helper must recursively free its children: free every char* field, free each element of every array (and the array itself), and call the child struct free helpers before freeing the parent. The generator orders helpers so children are defined first. Forgetting to free a nested level leaks; freeing the cJSON tree and your own struct in the wrong order double-frees. Treat the create/free pair as a transaction.
Why are arrays represented as a pointer plus a count field?
C arrays decay to pointers and carry no length information, so a JSON array of unknown size cannot be a single field. The idiomatic solution is a struct member like Item* items together with size_t items_count. The count tells loops where to stop and tells the free helper how many elements to release. This pointer + count pair is the same pattern used throughout the C standard library and POSIX APIs.
Is my data sent to a server?
No, all processing happens entirely in your browser. Your JSON data never leaves your device.
Real-world Examples
Parsing a sensor reading with cJSON in dynamic mode
An embedded gateway receives JSON telemetry over MQTT. Dynamic mode with cJSON stubs generates the struct plus a from_json helper that walks the parsed tree. Note the explicit NULL checks the parser needs and the strdup for the string field so the struct owns its own copy independent of the cJSON tree.
{
"deviceId": "sensor-0427",
"temperature": 21.4,
"humidity": 58,
"online": true,
"lastError": null
}#include <cjson/cJSON.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char *device_id;
double temperature;
int humidity;
int online; /* 0 or 1 */
char *last_error; /* NULL when JSON null */
} SensorReading;
SensorReading *sensor_reading_from_json(const cJSON *root) {
SensorReading *out = calloc(1, sizeof(SensorReading));
const cJSON *id = cJSON_GetObjectItemCaseSensitive(root, "deviceId");
if (cJSON_IsString(id) && id->valuestring)
out->device_id = strdup(id->valuestring);
out->temperature = cJSON_GetObjectItem(root, "temperature")->valuedouble;
out->humidity = cJSON_GetObjectItem(root, "humidity")->valueint;
out->online = cJSON_IsTrue(cJSON_GetObjectItem(root, "online"));
const cJSON *err = cJSON_GetObjectItem(root, "lastError");
out->last_error = cJSON_IsString(err) ? strdup(err->valuestring) : NULL;
return out;
}
void sensor_reading_free(SensorReading *s) {
if (!s) return;
free(s->device_id);
free(s->last_error);
free(s);
}A nested config object with an array, static mode
A config struct read once at startup where sizes are known and bounded. Static mode avoids the heap entirely: nested objects are embedded by value and the array of ports becomes a fixed buffer plus a count. No free helper is needed because nothing is malloc-ed.
{
"serviceName": "edge-proxy",
"maxConnections": 1024,
"network": {
"host": "0.0.0.0",
"ports": [8080, 8443, 9090]
}
}#include <stddef.h>
typedef struct {
char host[64];
int ports[16];
size_t ports_count;
} Network;
typedef struct {
char service_name[64];
int max_connections;
Network network; /* embedded by value, no pointer */
} Config;