Free & open source — no account required
Free & open source — no account required
This technical guide provides an in-depth analysis of the json to c struct engine, best practices for implementation, and data security standards.
When integrating a JSON API into a C project — whether for an embedded system, a Linux daemon, or a game engine — the first step is defining a typedef struct that mirrors the JSON schema. C has no reflection and no runtime type system, so every field mapping must be written by hand unless you generate it. TypeMorph reads your JSON sample, infers C types, and outputs a complete typedef struct along with a cJSON parse function. Everything runs in your browser — your data never leaves your machine.
// Input JSON
{
"device_id": "sensor_042",
"temperature": 23.7,
"humidity": 61,
"is_active": true,
"firmware": "2.1.4",
"error_code": null
}
// Generated C (C99, cJSON)
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include "cJSON.h"
typedef struct {
char *device_id;
double temperature;
int32_t humidity;
bool is_active;
char *firmware;
char *error_code; /* nullable */
} Root;
Root root_from_json(const char *json_str) {
Root result = {0};
cJSON *root = cJSON_Parse(json_str);
if (!root) return result;
cJSON *_device_id = cJSON_GetObjectItemCaseSensitive(root, "device_id");
if (cJSON_IsString(_device_id)) result.device_id = _device_id->valuestring;
cJSON *_temperature = cJSON_GetObjectItemCaseSensitive(root, "temperature");
if (cJSON_IsNumber(_temperature)) result.temperature = _temperature->valuedouble;
cJSON *_humidity = cJSON_GetObjectItemCaseSensitive(root, "humidity");
if (cJSON_IsNumber(_humidity)) result.humidity = (int32_t)_humidity->valuedouble;
cJSON *_is_active = cJSON_GetObjectItemCaseSensitive(root, "is_active");
if (cJSON_IsBool(_is_active)) result.is_active = cJSON_IsTrue(_is_active);
cJSON *_firmware = cJSON_GetObjectItemCaseSensitive(root, "firmware");
if (cJSON_IsString(_firmware)) result.firmware = _firmware->valuestring;
cJSON_Delete(root);
return result;
}
JSON's type system maps to C types as follows. The key difference from higher-level languages: C strings are pointers (char *) or fixed-size arrays, not value types — and you must manage their lifetime explicitly.
// JSON → C type mapping
// "string" → char * (points into cJSON tree; copy with strdup() for ownership)
// number → double (safe default; cast to int32_t if always whole)
// integer → int32_t (use int64_t for large IDs, uint32_t for sizes)
// boolean → bool (requires #include <stdbool.h>, C99+)
// null → char * = NULL (pointer null represents JSON null naturally)
// object → nested typedef struct
// array → T *items + int items_count
// String ownership — the common pattern:
typedef struct {
char *name; // borrowed: points into cJSON tree (freed with cJSON_Delete)
} BorrowedUser;
typedef struct {
char *name; // owned: allocated with strdup(), must be free()'d
} OwnedUser;
void parse_owned(const char *json_str, OwnedUser *out) {
cJSON *j = cJSON_Parse(json_str);
cJSON *name = cJSON_GetObjectItemCaseSensitive(j, "name");
if (cJSON_IsString(name)) {
out->name = strdup(name->valuestring); // take ownership
}
cJSON_Delete(j); // safe to delete — we own a copy
}
// Input JSON
{
"user_id": "usr_001",
"name": "Ada Lovelace",
"address": {
"street": "123 Babbage Ln",
"city": "London",
"zip": "EC1A 1BB"
}
}
// Generated C — nested structs defined first
typedef struct {
char *street;
char *city;
char *zip;
} RootAddress;
typedef struct {
char *user_id;
char *name;
RootAddress address;
} Root;
Root root_from_json(const char *json_str) {
Root result = {0};
cJSON *j = cJSON_Parse(json_str);
if (!j) return result;
cJSON *_user_id = cJSON_GetObjectItemCaseSensitive(j, "user_id");
if (cJSON_IsString(_user_id)) result.user_id = _user_id->valuestring;
cJSON *_name = cJSON_GetObjectItemCaseSensitive(j, "name");
if (cJSON_IsString(_name)) result.name = _name->valuestring;
cJSON *_address = cJSON_GetObjectItemCaseSensitive(j, "address");
if (cJSON_IsObject(_address)) {
cJSON *_street = cJSON_GetObjectItemCaseSensitive(_address, "street");
if (cJSON_IsString(_street)) result.address.street = _street->valuestring;
cJSON *_city = cJSON_GetObjectItemCaseSensitive(_address, "city");
if (cJSON_IsString(_city)) result.address.city = _city->valuestring;
cJSON *_zip = cJSON_GetObjectItemCaseSensitive(_address, "zip");
if (cJSON_IsString(_zip)) result.address.zip = _zip->valuestring;
}
cJSON_Delete(j);
return result;
}
// Input JSON
{
"name": "Server Rack A",
"sensor_ids": ["s001", "s002", "s003"],
"temperatures": [21.3, 22.1, 20.8]
}
// Generated C — array fields become pointer + count pairs
typedef struct {
char *name;
char **sensor_ids;
int sensor_ids_count;
double *temperatures;
int temperatures_count;
} Rack;
Rack rack_from_json(const char *json_str) {
Rack r = {0};
cJSON *j = cJSON_Parse(json_str);
if (!j) return r;
cJSON *_name = cJSON_GetObjectItemCaseSensitive(j, "name");
if (cJSON_IsString(_name)) r.name = _name->valuestring;
cJSON *_ids = cJSON_GetObjectItemCaseSensitive(j, "sensor_ids");
if (cJSON_IsArray(_ids)) {
r.sensor_ids_count = cJSON_GetArraySize(_ids);
r.sensor_ids = malloc(r.sensor_ids_count * sizeof(char *));
int i = 0;
cJSON *item;
cJSON_ArrayForEach(item, _ids) {
if (cJSON_IsString(item)) r.sensor_ids[i++] = item->valuestring;
}
}
cJSON *_temps = cJSON_GetObjectItemCaseSensitive(j, "temperatures");
if (cJSON_IsArray(_temps)) {
r.temperatures_count = cJSON_GetArraySize(_temps);
r.temperatures = malloc(r.temperatures_count * sizeof(double));
int i = 0;
cJSON *item;
cJSON_ArrayForEach(item, _temps) {
if (cJSON_IsNumber(item)) r.temperatures[i++] = item->valuedouble;
}
}
// NOTE: Don't cJSON_Delete(j) here — strings point into the cJSON tree.
// Either copy them with strdup(), or keep cJSON alive until done with r.
return r;
}
When should I use C instead of C++? Use C when targeting embedded systems (bare-metal, RTOS), writing kernel modules or drivers, or working in environments where C++ runtime overhead is unacceptable. C is also the right choice when interfacing with other languages via FFI — nearly every language can call a C ABI. If you have a choice and the environment supports it, C++ gives you std::string and RAII which eliminate many of the memory management pitfalls.
What is cJSON and why use it? cJSON is a single-file, MIT-licensed JSON parser for C. It's the most widely used C JSON library, found in ESP-IDF (ESP32), Zephyr RTOS, and countless embedded projects. Add it to your project by copying cJSON.c and cJSON.h — no build system changes required. Alternatives include jansson (owned strings, higher-level API) and jsmn (zero-allocation, token-based).
Why does the generated code use char * instead of char name[256]? Pointer strings are more flexible for API responses where string lengths vary. Fixed-size arrays (char name[64]) are better for embedded targets where malloc is unavailable or undesirable. For embedded use, change char * fields to char field[MAX_LEN] and use strncpy instead of direct pointer assignment.
Is my JSON sent to a server? No. TypeMorph runs entirely in your browser — none of your data leaves your machine.
Is the processing local-only?
Absolutely. TypeMorph operates entirely within your browser's sandbox. We use Web Workers for high-performance computation without ever transmitting your JSON, SQL, or API data to a remote server.
Can I use this for enterprise projects?
Yes. The tool is designed for professional software engineers who require GDPR compliance and data privacy. It is trusted by developers at top-tier startups and financial institutions.