<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged rtos</title>
<link>https://coderlegion.com/tag/rtos</link>
<description>Powered by Question2Answer</description>
<item>
<title>JsonX: Lightweight JSON to C struct mapping for Embedded Systems</title>
<link>https://coderlegion.com/6601/jsonx-lightweight-json-to-c-struct-mapping-for-embedded-systems</link>
<description>&lt;h3 id=&quot;jsonx-lightweight-json-c-struct-mapping-for-embedded-systems&quot;&gt;JsonX: Lightweight JSON → C struct mapping for Embedded Systems&lt;/h3&gt;
&lt;p&gt;JsonX is a lightweight C library for embedded developers who need to map JSON directly into structs — without dynamic memory or code generators.&lt;/p&gt;
&lt;p&gt;JSON is everywhere - from web APIs to IoT devices.&lt;/p&gt;
&lt;p&gt;But on microcontrollers, most JSON libraries feel like elephants in a china shop: they assume megabytes of RAM, malloc on every step, and a friendly OS.&lt;/p&gt;
&lt;p&gt;Typical JSON libraries for embedded systems fall into two extremes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Full-featured giants (e.g. &lt;strong&gt;ArduinoJson, jansson&lt;/strong&gt;): powerful but memory-hungry.&lt;/li&gt;
&lt;li&gt;Bare parsers (e.g. &lt;strong&gt;JSMN, cJSON, jfes&lt;/strong&gt;): no mapping, just tokenization.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;-the-problem-with-json-on-embedded-devices&quot;&gt; The Problem with JSON on Embedded Devices&lt;/h3&gt;
&lt;p&gt;When you work with RTOS or bare-metal firmware where every byte and cycle counts.&lt;br&gt;
That’s where &lt;strong&gt;JsonX&lt;/strong&gt; comes in - a lightweight layer over &lt;strong&gt;cJSON&lt;/strong&gt; designed specifically for MCU + RTOS environments, where predictable memory and simplicity matter most.&lt;/p&gt;
&lt;p&gt;So you either waste RAM or write tons of manual traversal code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// classical pain
value = cJSON_GetObjectItem(root, &quot;config&quot;);
param = cJSON_GetObjectItem(value, &quot;baudrate&quot;);
baudrate = param-&amp;gt;valueint;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;-jsonx-in-one-sentence&quot;&gt; JsonX in One Sentence&lt;/h3&gt;
&lt;p&gt;A thin mapping layer that turns JSON into C structs using a declarative table, not hand-written traversal.&lt;/p&gt;
&lt;p&gt;Example structure:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;typedef struct
{
    char      device_name[32];
    double    baudrate;
    bool      debug;
} config_t;

config_t config;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Mapping:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;JX_ELEMENT config_desc[] =
{
    JX_PROPERTY_STRING(&quot;device_name&quot;, config.device_name),
    JX_PROPERTY_NUMBER(&quot;baudrate&quot;, config.baudrate),
    JX_PROPERTY_BOOLEAN(&quot;debug&quot;, config.debug),
};

const size_t config_desc_size = sizeof(config_desc)/sizeof(config_desc[0]);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that’s it - one call:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const char *config_json_str = &quot;{\&quot;device_name\&quot;:\&quot;sensor1\&quot;,\&quot;baudrate\&quot;:9600,\&quot;debug\&quot;:true}&quot;;

if(jx_json_to_struct(char *buffer, config_desc, config_desc_size, JX_MODE_STRICT) == JX_SUCCESS)
{
    jx_log(&quot;Configuration loaded: %s @ %lu\n&quot;, config.device_name, (uint32_t)config.baudrate);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No malloc() surprises, no deep tree traversal.&lt;/p&gt;
&lt;h3 id=&quot;-core-benefits&quot;&gt; Core Benefits&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;✅ &lt;strong&gt;Predictable Memory&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;All allocations go through RTOS pools or a static buffer.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No hidden malloc/free. Works under &lt;strong&gt;ThreadX, FreeRTOS, or bare-metal&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;✅ &lt;strong&gt;Automatic Struct Mapping&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Define once → parse many.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Shared parsing engine for all schemas.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;✅ &lt;strong&gt;Optional / Ignored Fields&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Unknown keys ignored automatically.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Optional fields can have defaults.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Makes firmware tolerant to JSON changes.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;✅ &lt;strong&gt;Readable and Debuggable&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You still get readable JSON in logs - easier to verify on target or&lt;br&gt;
via serial output.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;-jsonx-vs-binary-formats&quot;&gt;⚖️ JsonX vs Binary Formats&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;When NOT to use JsonX:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You control both ends and need max bandwidth (LoRa, NB-IoT)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You rely on schema versioning (protobuf/flatbuffers)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;When JsonX shines:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You want human-readable logs&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You interface with web APIs&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You want deterministic memory use&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You prefer C-only solution&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;-example-parsing-an-array-of-coordinates&quot;&gt; Example: Parsing an Array of Coordinates&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;    #define JX_USER_BUFFER_SIZE    1024    
    // Initialize JsonX (using standard malloc/free)
    jx_init();
    
    // Define point structure
    typedef struct {
        double x;
        double y;
    } Point_t;
        
    // Define square structure with array of 4 points
    typedef struct {
        Point_t points[4];
    } Square_t;
        
        
    // Create square data structure
    Square_t square = {
        .points = {
            {0, 0},
            {1, 0},
            {1, 1},
            {0, 1}
        }
    };
        
    // Define JSON mapping for a single point object
    JX_ELEMENT point_0[] = {
        JX_PROPERTY_NUMBER(&quot;x&quot;, square.points[0].x),
        JX_PROPERTY_NUMBER(&quot;y&quot;, square.points[0].y)
    };
    
    JX_ELEMENT point_1[] = {
        JX_PROPERTY_NUMBER(&quot;x&quot;, square.points[1].x),
        JX_PROPERTY_NUMBER(&quot;y&quot;, square.points[1].y)
    };
    
    JX_ELEMENT point_2[] = {
        JX_PROPERTY_NUMBER(&quot;x&quot;, square.points[2].x),
        JX_PROPERTY_NUMBER(&quot;y&quot;, square.points[2].y)
    };
    
    JX_ELEMENT point_3[] = {
        JX_PROPERTY_NUMBER(&quot;x&quot;, square.points[3].x),
        JX_PROPERTY_NUMBER(&quot;y&quot;, square.points[3].y)
    };
    
    // Define array of point objects
    JX_ELEMENT square_array[] = {
        JX_OBJECT_VAL(point_0),
        JX_OBJECT_VAL(point_1),
        JX_OBJECT_VAL(point_2),
        JX_OBJECT_VAL(point_3)
    };
    
    // Define root object with &quot;square&quot; property
    JX_ELEMENT root[] = {
        JX_PROPERTY_ARRAY(&quot;square&quot;, square_array)
    };
    
    // ===== SERIALIZATION: C struct -&amp;gt; JSON =====
    char *json_output = jx_alloc_memory(JX_USER_BUFFER_SIZE);
    if (json_output != NULL) {
        jx_struct_to_json(root, 1, json_output, JX_USER_BUFFER_SIZE, JX_FORMATTED);
        jx_log(&quot;Serialized JSON:\n%s\n\n&quot;, json_output);
        jx_free_memory(json_output);
    }
        
    // ===== DESERIALIZATION: JSON -&amp;gt; C struct =====
    const char *json_input =
    &quot;{&quot;
        &quot;  \&quot;square\&quot;: [&quot;
            &quot;    {\&quot;x\&quot;: 5, \&quot;y\&quot;: 6},&quot;
            &quot;    {\&quot;x\&quot;: 7, \&quot;y\&quot;: 8},&quot;
            &quot;    {\&quot;x\&quot;: 9, \&quot;y\&quot;: 10},&quot;
            &quot;    {\&quot;x\&quot;: 11, \&quot;y\&quot;: 12}&quot;
        &quot;  ]&quot;
    &quot;}&quot;;
    
    
    // Parse JSON
    jx_json_to_struct((char*)json_input, root, 1, JX_MODE_STRICT);
    
    // Print parsed data
    jx_log(&quot;Parsed square points:\n&quot;);
    for (int i = 0; i &amp;lt; 4; i++) {
        jx_log(&quot;  Point %d: x=%d, y=%d\n&quot;,
                    i,
                    (int)square.points[i].x,
                    (int)square.points[i].y);
        }
    // Cleanup
    jx_parser_deinit();
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;-tested-configuration-stm32h7-threadx--os-gcc-12-&quot;&gt; Tested Configuration: STM32H7 + ThreadX (-Os, GCC 12)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;⚙️ Memory &amp;amp; Code Footprint&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Verified on STM32H7 (GCC 12, -Os, ThreadX)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;jx_parser.o&lt;/strong&gt; — 2228 B code, 4 B static RAM&lt;br&gt;
Core parser and mapping logic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;jx_static_allocator.o&lt;/strong&gt; — 248 B code, 4 B static RAM&lt;br&gt;
Implements static allocator for bare-metal mode.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;jx_version.o&lt;/strong&gt; — 68 B code, 0 B RAM&lt;br&gt;
Simple version string helper.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;example.o (demo)&lt;/strong&gt; — 765 B code, 1076 B RAM&lt;br&gt;
Demo test including a 1 KB byte pool for ThreadX.&lt;/p&gt;
&lt;p&gt;➡ &lt;strong&gt;Core only&lt;/strong&gt;: ≈ 2.5 KB code + 8 B static RAM&lt;/p&gt;
&lt;p&gt;➡ &lt;strong&gt;With example&lt;/strong&gt;: ≈ 3.2 KB code + 1.06 KB RAM&lt;/p&gt;
&lt;p&gt;The demo includes a 1 KB jx_byte_pool_buffer for ThreadX, but the actual pool size is user-defined.&lt;br&gt;
JsonX itself has &lt;strong&gt;no hidden heap allocations&lt;/strong&gt; - all memory management is deterministic.&lt;/p&gt;
&lt;p&gt;The static allocator consumes only ≈ &lt;strong&gt;12–16 bytes&lt;/strong&gt; of header overhead inside the provided buffer.&lt;/p&gt;
&lt;h3 id=&quot;-open-source-and-final-thoughts&quot;&gt; Open Source and Final Thoughts&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/embedmind/JsonX&quot;&gt;https://github.com/embedmind/JsonX&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;JsonX&lt;/strong&gt; is lightweight, open-source (&lt;strong&gt;MIT&lt;/strong&gt;), and built purely in C for portability across RTOSes and MCU families.&lt;/p&gt;
&lt;h3 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;/h3&gt;
&lt;p&gt;This library isn’t about inventing a new protocol - it’s about making life easier for embedded developers who already have to deal with JSON.&lt;/p&gt;
&lt;p&gt;Working with JSON on MCUs? I'd love to hear your use case - especially if you've battled with JSMN, Jansson, or ArduinoJson before.&lt;/p&gt;
&lt;h3 id=&quot;-join-the-discussion&quot;&gt; Join the Discussion&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;⭐ Star on &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/embedmind/JsonX&quot;&gt;GitHub&lt;/a&gt; if this solves your problem&lt;/li&gt;
&lt;li&gt;Found a bug? Open an &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/embedmind/JsonX/issues&quot;&gt;issue&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Java</category>
<guid isPermaLink="true">https://coderlegion.com/6601/jsonx-lightweight-json-to-c-struct-mapping-for-embedded-systems</guid>
<pubDate>Fri, 17 Oct 2025 20:51:57 +0000</pubDate>
</item>
</channel>
</rss>