
作者?|??NevermindZZT
json是目前最為流行的文本數據傳輸格式,特別是在網絡通信上廣泛應用,隨著物聯網的興起,在嵌入式設備上,也需要開始使用json進行數據傳輸,那么,如何快速簡潔地用C語言進行json的序列化和反序列化呢?
當前,應用最廣泛的C語言json解析庫當屬cJSON,但是,使用cJSON讀json進行序列化和反序列化,需要根據key一個一個進行處理,會導致代碼冗余,邏輯性不強,哪有沒有更好的方法呢?
在Android平臺,一般會使用gson等工具解析json,這些工具將json直接映射成對象,在C語言上使用對象的概念,我們需要借助結構體,然而,最大的問題在于,C語言沒有高級語言具有的反射機制,直接從json映射到結構體對象幾乎是不可能的。
怎么解決呢,既然C語言沒有反射機制,那么我們可以自己定義一套類似于反射的機制,這里我將其稱之為結構體數據模型,在數據模型中,我們需要準確地描述結構體的特征,包括結構體各成員的名稱,類型,在結構體中的偏移。
有了這些,我們可以在解析josn的時候,將解析得到的數據直接寫入到對應的內存里面去,或者是在序列化的時候,直接從對應的內存中讀取數據,進行處理。
CSON正是采用上面說到的思路,使用數據模型對結構體進行描述,然后基于cJSON,根據數據模型進行解析,將解析得到的數據直接寫入到對應的內存區域,從而實現從json到結構體對象的映射。
CSON最基本的數據模型定義如下:
typedef?struct?cson_model
{
????CsonType?type;??????????????????????/**
????char?*key;??????????????????????????/**
????short?offset;???????????????????????/**
}?CsonModel;
通過type描述結構體成員的數據類型,key描述該成員在json中對應的字段,offset描述該結構體成員在結構體中的偏移,CSON在解析json的時候,根據type調用相應的cJSON API并傳遞key作為參數,得到解析出的數據,然后根據offset將數據寫入到對應的內存空間。
比如說這樣一個結構體:
struct?project
{
????int?id;
????char?*name;
}
該結構體包含兩個成員,對于成員id,我們使用數據模型對其進行描述
{.type=CSON_TYPE_CHAR,?key="id",?offset=0}
對于結構體的每個成員,都進行數據模型的定義,就可以得到一個完整的結構體數據模型,CSON會根據這個模型,進行解析。
因為是通過直接寫內存的方式,所以在寫不同類型的量到內存中時,會多次用到強制轉型,導致CSON中賦值的代碼都類似于:
*(int?*)((int)obj?+?model[i].offset)?=?(int)csonDecodeNumber(json,?model[i].key);
當然,上面說到的數據模型,只適用于基本數據類型的數據,對于子結構體,鏈表,數組等,需要對數據模型的定義進行擴充,有興趣的朋友可以直接閱讀CSON源碼。
/**?項目結構體?*/
struct?project
{
????int?id;
????char?*name;
};
/**?倉庫結構體?*/
struct?hub
{
????int?id;
????char?*user;
????struct?project?*cson;
};
對每一個需要使用cson的結構體,都需要定義相對應的數據模型
/**?項目結構體數據模型?*/
CsonModel?projectModel[]?=
{
????CSON_MODEL_OBJ(struct?project),
????CSON_MODEL_INT(struct?project,?id),
????CSON_MODEL_STRING(struct?project,?name),
};
/**?倉庫結構體數據模型?*/
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))
};
只需要定義好數據模型,就可以使用CSON讀json進行序列化和反序列化
void?csonDemo(void)
{
????char?*jsonDemo?=?"{\"id\":?1,?\"user\":?\"Letter\",?\"cson\":?{\"id\":?2,?\"name\":?\"cson\"}}";
????/**?解析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);
????/**?序列化對象?*/
????char?*formatJson?=?csonEncodeFormatted(pHub,?hubModel,?sizeof(hubModel)/sizeof(CsonModel));
????printf("format?json:?%s\r\n",?formatJson);
????/**?釋放結構體對象?*/
????csonFree(pHub,?hubModel,?sizeof(hubModel)/sizeof(CsonModel));
????/**?釋放序列化生成的json字符串?*/
????csonFreeJson(formatJson);
}
hub:?id:?1,?user:?Letter,?project?id:?2,?project?name:?cson
format?json:?{
????????"id":???1,
????????"user":?"Letter",
????????"cson":?{
????????????????"id":???2,
????????????????"name":?"cson"
????????}
}
可以看到,無論是解析json,還是序列化結構體到json,在使用CSON的情況下,都只需要一行代碼就可以解決,同樣的操作,在使用原生cJSON的情況下,你可能需要多次判斷,解析元素。
?https://github.com/NevermindZZT/cson
?
來源:https://blog.csdn.net/qq_34245464/
???????????????? ?END ????????????????
關注我的微信公眾號,回復“加群”按規則加入技術交流群。
點擊“閱讀原文”查看更多分享,歡迎點分享、收藏、點贊、在看。