In embedded development, do you often encounter the dilemma: the device has limited memory, and you want to use JSON, a universal data format, but are deterred by the “bulkiness” of mainstream libraries?
Don’t worry, today I will introduce a lightweight JSON parsing/generating library specifically designed for resource-constrained scenarios—tiny-json, allowing you to easily handle JSON on MCUs and IoT devices!
1. What is tiny-json?
In simple terms, “tiny-json” is alightweight JSON parsing library with three core goals: small, efficient, and fast.
It is designed for embedded systems, IoT devices, and small clients in resource-constrained environments, perfectly avoiding the size redundancy issues caused by the comprehensive functionality of mainstream JSON libraries (such as CJSON, nlohmann/json). Its official open-source address is: https://github.com/rafagafe/tiny-json, where developers can obtain the source code, examples, and related documentation.
2. Core Advantages of tiny-json: Why is it Suitable for Embedded Development?
Developers who have used mainstream JSON libraries know that while they are feature-rich, they are often not suitable for embedded devices. The advantages of tiny-json precisely address the pain points of resource-constrained scenarios:
- • Extremely Small Size
The source code typically consists of just 1-2 files (.h + .c), with a streamlined codebase, and the compiled size usually occupies less than 10KB of ROM, making it very friendly for storage-sensitive embedded devices!
- • Memory Friendly to the Extreme
It does not rely on dynamic memory allocation such as malloc/new, operating on a static buffer provided by the user, fundamentally avoiding memory leaks; it does not create complex intermediate objects, directly parsing based on the raw JSON string, with RAM usage as low as a few dozen to a few hundred bytes, making it “stress-free” for MCUs.
- • Focused Core Functionality
It only implements the essential JSON syntax (objects, arrays, strings, numbers, booleans, null), without any “fancy” features like comments, special encoding, or formatted output, minimizing complexity.
- • Cross-Platform and Dependency-Free
Implemented in pure C, it does not depend on any specific operating system or hardware, whether it’s STM32, Arduino, or small programs on Linux/macOS, making it easy to port.
- • No Recursive Design
The parsing process does not use recursion, avoiding potential stack overflow issues on resource-limited embedded devices, resulting in more stable operation.
- • Supports Nested Structures
Although lightweight, it imposes no hierarchical limits on nested objects and arrays in JSON, meeting the parsing needs of complex data structures.
3. How Does tiny-json Compare to Other Libraries?
Here’s a direct comparison:
| Library Name | Advantages | Disadvantages | Suitable Scenarios |
| cJSON | User-friendly API, comprehensive functionality | High memory usage due to dynamic allocation | Embedded devices with relatively abundant resources |
| tiny-json | Static allocation, zero-copy, ultra-lightweight, no recursion | Streamlined functionality, no extensibility features | Memory-constrained MCUs, IoT devices |
In simple terms, if you are developing for resource-constrained small devices, tiny-json is almost the optimal solution!
4. Practical Demonstration: How to Use tiny-json?
Talk is cheap; let’s look at specific examples to see how tiny-json parses JSON data.
Example 1: Basic Parsing (Retrieving Simple Fields)
Assuming we have a JSON string containing a name and age, the parsing steps are as follows:
Step 1: Define the JSON String and Static Memory Pool
enum { MAX_FIELDS = 4 };
json_t pool[MAX_FIELDS]; // Static memory pool to store JSON nodes
char str[] = "{ \"name\": \"peter\", \"age\": 32 }";
Step 2: Parse the JSON String
json_t const* parent = json_create(str, pool, MAX_FIELDS);
if (parent == NULL) return EXIT_FAILURE; // Handle parsing failure
Step 3: Extract String Type Field (Name)
json_t const* namefield = json_getProperty(parent, "name");
if (namefield == NULL || json_getType(namefield) != JSON_TEXT) {
// Handle field not existing or type error
}
char const* namevalue = json_getValue(namefield); // Get value (string)
printf("Name: %s\n", namevalue); // Output: peter
Step 4: Extract Integer Type Field (Age)
json_t const* agefield = json_getProperty(parent, "age");
if (agefield == NULL || json_getType(agefield) != JSON_INTEGER) {
// Handle field not existing or type error
}
int64_t agevalue = json_getInteger(agefield); // Get value (integer)
printf("Age: %lld\n", agevalue); // Output: 32
Example 2: Parsing Nested Objects and Arrays
For complex JSON containing nested objects (like address) and arrays (like phone lists), the parsing method is as follows (core steps):
Step 1: Define Complex JSON String
char str[] = "{\n"
"\t\"firstName\": \"Bidhan\",\n"
"\t\"lastName\": \"Chatterjee\",\n"
"\t\"age\": 40,\n"
"\t\"address\": {\n"
"\t\t\"streetAddress\": \"144 J B Hazra Road\",\n"
"\t\t\"city\": \"Burdwan\"\n"
"\t},\n"
"\t\"phoneList\": [\n"
"\t\t{ \"type\": \"personal\", \"number\": \"09832209761\" },\n"
"\t\t{ \"type\": \"fax\", \"number\": \"91-342-2567692\" }\n"
"\t]\n"
"};
Step 2: Parse Nested Object (Address)
json_t const* address = json_getProperty(json, "address");
if (address && json_getType(address) == JSON_OBJ) {
char const* city = json_getPropertyValue(address, "city");
printf("City: %s\n", city); // Output: Burdwan
}
Step 3: Iterate Over Array (Phone List)
json_t const* phoneList = json_getProperty(json, "phoneList");
if (phoneList && json_getType(phoneList) == JSON_ARRAY) {
json_t const* phone;
// Use json_getChild (first element) and json_getSibling (next element) to iterate
for (phone = json_getChild(phoneList); phone != 0; phone = json_getSibling(phone)) {
if (json_getType(phone) == JSON_OBJ) {
char const* number = json_getPropertyValue(phone, "number");
printf("Phone Number: %s\n", number);
}
}
}
Output Results
The final program will output the parsed information:
Name: peter
Age: 32
City: Burdwan
Phone Number: 09832209761
Phone Number: 91-342-2567692
5. Detailed Overview of tiny-json API
The tiny-json API is concise and intuitive, with the core interfaces as follows:
- • json_create(): Parses a JSON string, with parameters for the JSON string, static memory pool, and maximum field count, returning the root node.
- • json_getProperty(): Retrieves a child node based on the field name, used for object types.
- • json_getType(): Gets the node type (JSON_OBJ, JSON_ARRAY, JSON_TEXT, etc.).
- • json_getValue(): Retrieves the string representation of the node’s value (applicable to all types).
- • json_getInteger(): Retrieves the value of an integer type node (int64_t).
- • json_getChild(): Retrieves the first element node of an array.
- • json_getSibling(): Retrieves the next sibling node of the current node (used for array iteration).
6. Conclusion: Why is tiny-json Worth Using?
From the above content, it is clear that tiny-json’s design is very much aligned with the needs of embedded development:
- • It avoids dynamic allocation by using a static memory pool, reducing memory fragmentation and leak risks;
- • The interface is simple and intuitive, focusing on the core need of “parsing JSON”, lowering the learning and usage costs;
- • Its size and memory usage are extremely small, allowing smooth operation even on resource-constrained devices;
- • It supports nested structures and arrays, meeting the data parsing needs of most embedded scenarios.
If you are developing projects for MCUs, IoT devices, or other resource-constrained environments and need to handle JSON data, be sure to check out its official repository (https://github.com/rafagafe/tiny-json); tiny-json is definitely a lightweight option worth trying!