Essential for Embedded Development! A Detailed Overview of the Lightweight JSON Library tiny-json, Your Savior in Memory-Constrained Scenarios

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!

Leave a Comment