C Language: cJSON and Struct Conversion

1 Introduction

JSON is currently the most popular text data transmission format, widely used in network communication. With the rise of the Internet of Things, embedded devices also need to start using JSON for data transmission. So, how can we quickly and simply perform JSON serialization and deserialization in C language?

Currently, the most widely used C language JSON parsing library is cJSON. However, using cJSON to read JSON for serialization and deserialization requires processing each key one by one, which leads to redundant code and weak logic. Is there a better method?

2 Approach

On the Android platform, tools like Gson are generally used to parse JSON. These tools directly map JSON to objects. In C language, we need to use structures to achieve this. However, the biggest problem is that C language lacks the reflection mechanism found in high-level languages, making it nearly impossible to directly map JSON to structure objects.

How can we solve this? Since C language does not have a reflection mechanism, we can define our own set of mechanisms similar to reflection. Here, I will refer to it as a structure data model. In this data model, we need to accurately describe the characteristics of the structure, including the names, types, and offsets of each member in the structure.

With this information, we can write the parsed data directly into the corresponding memory when parsing JSON, or read data directly from the corresponding memory during serialization for processing.

3 Implementation

CSON adopts the approach mentioned above, using a data model to describe structures, and then based on cJSON, parsing according to the data model, writing the parsed data directly into the corresponding memory area, thus achieving the mapping from JSON to structure objects.

The most basic definition of the data model in CSON is as follows:

typedef struct cson_model {    CsonType type;                      /**< Data type */char *key;                          /**< Element key */short offset;                       /**< Element offset */} CsonModel;

The type describes the data type of the structure member, key describes the corresponding field in JSON, and offset describes the offset of that structure member within the structure. When parsing JSON, CSON calls the corresponding cJSON API based on type and passes key as a parameter to obtain the parsed data, then writes the data into the corresponding memory space based on offset.

For example, consider the following structure:

struct project {int id;char *name;};

This structure contains two members. For the member id, we describe it using the data model:

{.type = CSON_TYPE_CHAR, key = "id", offset = 0}

By defining a data model for each member of the structure, we can obtain a complete structure data model, which CSON will use for parsing.

Since this method involves directly writing to memory, when writing different types of values to memory, we will frequently use type casting, resulting in CSON’s assignment code being similar to:

*(int *)((int)obj + model[i].offset) = (int)csonDecodeNumber(json, model[i].key);

Of course, the data model mentioned above only applies to basic data types. For sub-structures, linked lists, arrays, etc., the definition of the data model needs to be expanded. Interested readers can directly read the CSON source code.

4 CSON Usage Example

Declare Structures:

/** Project structure */struct project {int id;char *name;};/** Repository structure */struct hub {int id;char *user;struct project *cson;};

Define Data Models:

For each structure that needs to use CSON, a corresponding data model must be defined.

/** Project structure data model */CsonModel projectModel[] = {    CSON_MODEL_OBJ(struct project),    CSON_MODEL_INT(struct project, id),    CSON_MODEL_STRING(struct project, name),};/** Repository structure data model */CsonModel hubModel[] = {    CSON_MODEL_OBJ(struct hub),    CSON_MODEL_INT(struct hub, id),    CSON_MODEL_STRING(struct hub, user),    CSON_MODEL_STRUCT(struct hub, cson, projectModel, sizeof(projectModel)/sizeof(CsonModel))};

Using CSON for Parsing:

Once the data model is defined, CSON can be used to read JSON for serialization and deserialization.

void csonDemo(void) {char *jsonDemo = "{\"id\": 1, \"user\": \"Letter\", \"cson\": {\"id\": 2, \"name\": \"cson\"}}";/** Parse JSON */struct hub *pHub = csonDecode(jsonDemo, hubModel, sizeof(hubModel)/sizeof(CsonModel));printf("hub: id: %d, user: %s, project id: %d, project name: %s\r\n",        pHub->id, pHub->user, pHub->cson->id, pHub->cson->name);/** Serialize object */char *formatJson = csonEncodeFormatted(pHub, hubModel, sizeof(hubModel)/sizeof(CsonModel));printf("format json: %s\r\n", formatJson);/** Free structure object */csonFree(pHub, hubModel, sizeof(hubModel)/sizeof(CsonModel));/** Free serialized JSON string */csonFreeJson(formatJson);}

Run Results:

hub: id: 1, user: Letter, project id: 2, project name: csonformat json: {"id": 1,"user": "Letter","cson": {"id": 2,"name": "cson"}}

As we can see, whether parsing JSON or serializing structures to JSON, using CSON allows us to solve the problem with just one line of code. In contrast, using native cJSON may require multiple checks and element parsing.

5 CSON Address

https://github.com/NevermindZZT/csonSource: https://blog.csdn.net/qq_34245464

Leave a Comment