目錄
1. JSON
1.1 JSON 概述
JSON(JavaScript Object Notation) 是一種輕量級的數據交換格式。 易於人閱讀和編寫。同時也易於機器解析和生成。 它基於JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一個子集。 JSON採用完全獨立於語言的文本格式,但是也使用了類似於C語言家族的習慣(包括C, C++, C#, Java, JavaScript, Perl, Python等)。 這些特性使JSON成爲理想的數據交換語言。
JSON建構於兩種結構:
-
“名稱/值”對的集合(A collection of name/value pairs)。不同的語言中,它被理解爲對象(object),紀錄(record),結構(struct),字典(dictionary),哈希表(hash table),有鍵列表(keyed list),或者關聯數組 (associative array)。
-
值的有序列表(An ordered list of values)。在大部分語言中,它被理解爲數組(array)。
這些都是常見的數據結構。事實上大部分現代計算機語言都以某種形式支持它們。這使得一種數據格式在同樣基於這些結構的編程語言之間交換成爲可能。
官方網站:http://www.json.org/
1.2 JSON 語法
JSON 語法是 JavaScript 語法的子集。
- 對象表示爲鍵值對
- 數據由逗號分隔
- 大括號保存對象
- 中括號保存數組
1.2.1 JSON 對象
對象是一個無序的“‘名稱/值’對”集合。一個對象以 {
左括號 開始, }
右括號 結束。每個“名稱”後跟一個 :
冒號 ;“‘名稱/值’ 對”之間使用 ,
逗號 分隔。
對象可以包含多個 key/value(鍵/值)對。
key 必須是字符串,value 可以是合法的 JSON 數據類型(字符串, 數字, 對象, 數組, 布爾值或 null)。
key 和 value 中使用冒號(:)分割。
每個 key/value 對使用逗號(,)分割。
{ "name":"zhangsan" , "age": 30 }
使用點號(.)來訪問對象的值
var x = obj.name
1.2.2 JSON 數組
數組是值(value)的有序集合。一個數組以 [
左中括號 開始, ]
右中括號 結束。值之間使用 ,
逗號 分隔。
數組可包含多個對象。
JSON 中數組值必須是合法的 JSON 數據類型(字符串, 數字, 對象, 數組, 布爾值或 null)。
{
"sites": [
{ "name":"百度" , "url":"www.baidu.com" },
{ "name":"google" , "url":"www.google.com" },
{ "name":"微博" , "url":"www.weibo.com" }
]
}
對象 "sites" 是包含三個對象的數組。每個對象代表一條關於某個網站(name、url)的記錄。
1.2.3 JSON 值
值(value)可以是雙引號括起來的字符串(string)、數值(number)、true
、false
、 null
、對象(object)或者數組(array)。這些結構可以嵌套。
1.2.4 JSON 字符串
字符串(string)是由雙引號包圍的任意數量Unicode字符的集合,使用反斜線轉義。一個字符(character)即一個單獨的字符串(character string)。
字符串(string)與C或者Java的字符串非常相似。
1.2.5 JSON 數值
數值(number)也與C或者Java的數值非常相似。可以是整型或者浮點型:數值必須以十進制表示,不支持八進制與十六進制格式。
{ "age": 30 }
1.2.6 JSON 布爾值
JSON 布爾值可以是 true 或者 false:
{ "flag":true }
1.2.7 JSON null
JSON 可以設置 null 值:
{ "value":null }
1.2.8 JSON 空白
空白可以加入到任何符號之間。 以下描述了完整的語言。
1.2.9 JSON 文件 文件
JSON 文件的文件類型是 ".json"
2. CJSON獲取和移植
2.1 源碼獲取
如果查看過JSON 官網的可以發現,語法之後,羅列了多種語言相關的JSON支持,其中就包含SJSON,直接點擊就可以跳轉到相關的源碼GitHUB位置。
當然亦可以直接點擊下面的Github鏈接:https://github.com/DaveGamble/cJSON
cJSON 是一個用於解析JSON 包的C 語言庫,庫文件爲cJSON.c 和cJSON.h, 所有的實現都在這兩個文件中。
2.2 CJSON 移植
CJSON 中默認C語言庫的動態內存分配與釋放函數,但在嵌入式系統中明顯不太適合,需要更改爲具體適應操作系統的,這裏以freertos爲例:
官方源碼:
針對freertos 修改後:
#if defined(_MSC_VER)
/* work around MSVC error C2322: '...' address of dillimport '...' is not static */
static void * CJSON_CDECL internal_malloc(size_t size)
{
// return malloc(size);
return pvPortMalloc(size);
}
static void CJSON_CDECL internal_free(void *pointer)
{
// free(pointer);
vPortFree(pointer);
}
static void * CJSON_CDECL internal_realloc(void *pointer, size_t size)
{
// return realloc(pointer, size);
return NULL;
}
#else
#define internal_malloc pvPortMalloc
#define internal_free vPortFree
#define internal_realloc
#endif
這樣就移植好了,很簡單。
3 CJSON 使用
3.1 CJSON 結構體
/* The cJSON structure: */
typedef struct cJSON
{
/* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
struct cJSON *next;
struct cJSON *prev;
/* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
struct cJSON *child;
/* The type of the item, as above. */
int type;
/* The item's string, if type==cJSON_String and type == cJSON_Raw */
char *valuestring;
/* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
int valueint;
/* The item's number, if type==cJSON_Number */
double valuedouble;
/* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
char *string;
} cJSON;
/* cJSON Types: */
#define cJSON_Invalid (0)
#define cJSON_False (1 << 0)
#define cJSON_True (1 << 1)
#define cJSON_NULL (1 << 2)
#define cJSON_Number (1 << 3)
#define cJSON_String (1 << 4)
#define cJSON_Array (1 << 5)
#define cJSON_Object (1 << 6)
#define cJSON_Raw (1 << 7) /* raw json */
- cJOSN結構體爲一個雙向鏈表,並可通過child指針訪問下一層。
- .type :變量決定數據項類型(鍵的類型)
- valuestring / valueint / valuedouble: 數據項 依次表示是字符串/是整形/浮點型
-
string : 節點名稱,或理解爲“鍵(key)”
CJSON 作爲JSON格式的解析庫,功能無外乎構建和解析JSON格式,採用CJSON的設備,以JSON的格式發送數據,收到JSON格式的數據,解析成可供應用識別的功能。
3.2 CJSON 構建功能
3.2.1 對象構建
創建對象,並在對象中加入相關量。
void cjson_test ( void )
{
cJSON * usr;
char * out;
uint8_t stat = 0;
printf("\r\n--- CJSON Test %s---\r\n ",__TIME__);
usr = cJSON_CreateObject(); // 創建根數據對象
if (stat)
{
out = cJSON_Print(usr);
printf("\r\n%s\r\n", out);
}
cJSON_AddNullToObject(usr, "NULL"); // 加入null
if (stat)
{
out = cJSON_Print(usr);
printf("\r\n%s\r\n", out);
}
cJSON_AddTrueToObject(usr, "TURE"); // 加入ture
if (stat)
{
out = cJSON_Print(usr);
printf("\r\n%s\r\n", out);
}
cJSON_AddFalseToObject(usr, "FALSE"); // 加入false
if (stat)
{
out = cJSON_Print(usr);
printf("\r\n%s\r\n", out);
}
cJSON_AddBoolToObject(usr, "BOOL", 0); // 加入 bool
if (stat)
{
out = cJSON_Print(usr);
printf("\r\n%s\r\n", out);
}
cJSON_AddNumberToObject(usr, "INT_NUM", 10); // 加入 整數
if (stat)
{
out = cJSON_Print(usr);
printf("\r\n%s\r\n", out);
}
cJSON_AddNumberToObject(usr, "F_NUM", 9.9); // 加入浮點數
if (stat)
{
out = cJSON_Print(usr);
printf("\r\n%s\r\n", out);
}
cJSON_AddStringToObject(usr, "STRING", "This is string!"); // 加入字符串
if (stat)
{
out = cJSON_Print(usr);
printf("\r\n%s\r\n", out);
}
cJSON_AddObjectToObject(usr, "OBJECT"); // 加入對象
if (stat)
{
out = cJSON_Print(usr);
printf("\r\n%s\r\n", out);
}
cJSON_AddArrayToObject(usr, "ARRARY"); // 加入數組
out = cJSON_Print(usr);
// out = cJSON_PrintUnformatted(usr);
printf("%s", out);
cJSON_Delete(usr);
if(out)
vPortFree(out);
}
輸出結果:
- cJSON_CreateObject函數可創建一個根數據項,之後便可向該根數據項中添加string或int等內容,返回的是一個 cJSON的指針,注意,在這個指針用完了以後,需要手工調用 cJSON_Delete(usr); 進行內存回收 。
- 需要注意的是 json 格式的數據,雖然也是一個字符串的樣子,但這個時候還是無法當成普通的字符串進行使用,需要調用 cJSON_PrintUnformatted(usr) 或者 cJSON_Print(usr);來將json對象轉換成普通的字符串,並且都是以該json對象的根爲基點。兩個API的區別即是:一個是沒有格式的:也就是轉換出的字符串中間不會有"\n" "\t"之類的東西存在,而cJSON_Print(usr);打印出來是人看起來很舒服的格式。
- 函數內部封裝關聯freertos 動態內存分配函數,所以使用vPortFree函數釋放被out佔用的內存空間
3.2.2 數組構建
創建一個數組,並向數組添加一個字符串和一個數字
void cjson_test ( void )
{
cJSON * usr;
char * out;
uint8_t stat = 0;
printf("\r\n--- CJSON Test %s---\r\n ",__TIME__);
/* 創建根數據數組 */
usr = cJSON_CreateArray();
if (stat)
{
out = cJSON_Print(usr);
printf("\r\n%s\r\n", out);
}
cJSON_AddItemToArray(usr, cJSON_CreateString("hello cjson"));
cJSON_AddItemToArray(usr, cJSON_CreateNumber(99));
out = cJSON_Print(usr);
printf("%s", out);
cJSON_Delete(usr);
if(out)
vPortFree(out);
}
輸出結果:
3.3 CJSON 解析功能
解析的過程,其實就是構建的返過程。
3.3.1 root對象
先將普通的json串處理成json對象,也就是所謂的創建json root的過程,只有一行代碼即可。
cJSON * root;
root = cJSON_Parse(js_string);
cJSON_Delete(root);
根數據對象已經轉換完成,接下來就是獲取對象中的關鍵字(key),若多層嵌套,則逐層解析,類似剝洋蔥。
cJSON_Parse() 與 cJSON_Delete() 操作正常情況下,必須成對出現,否則將帶來內存泄漏。
3.3.2 一層對象
CJSON中type定義完全的,正常需要先判斷type再取值。
void cjson_test ( void )
{
cJSON * root, *js_name,*js_class,*js_age;
char * in_js_string = "{\"name\":\"laowang\",\"class\":\"3-2\",\"age\":10}";
printf("\r\n--- CJSON Test %s---\r\n ",__TIME__);
root = cJSON_Parse(in_js_string);
js_name = cJSON_GetObjectItem(root, "name");
js_class = cJSON_GetObjectItem(root, "class");
js_age = cJSON_GetObjectItem(root, "age");
printf("\r\nvalue(name:%s , class:%s, age:%d)\r\n", js_name->valuestring, js_class->valuestring, js_age->valueint);
printf("\r\ntpye(name:%d , class:%d, age:%d)\r\n", js_name->type, js_class->type, js_age->type);
cJSON_Delete(root);
}
輸出結果:
3.3.3 多層對象
多層本質上一樣的,只是多重複幾次而已。
int cjson_test ( void )
{
cJSON * root, *js_name,*js_class,*js_age, *num1,*num2;
char * in_js_string = "{\"num1\":{\"name\":\"laowang\",\"class\":\"3-2\",\"age\":10}, \"num2\":{\"name\":\"laoliu\",\"class\":\"3-1\",\"age\":11}}";
printf("\r\n--- CJSON Test %s---\r\n ",__TIME__);
root = cJSON_Parse(in_js_string);
if (!root)
{
printf("Get root object failed");
return -1;
}
num1 = cJSON_GetObjectItem(root, "num1");
if (!num1)
{
printf("Get num1 object failed");
return -1;
}
if (num1->type == cJSON_Object)
{
/* 對象 */
js_name = cJSON_GetObjectItem(num1, "name");
js_class = cJSON_GetObjectItem(num1, "class");
js_age = cJSON_GetObjectItem(num1, "age");
printf("\r\n num1(name:%s , class:%s, age:%d)\r\n", js_name->valuestring, js_class->valuestring, js_age->valueint);
}
num2 = cJSON_GetObjectItem(root, "num2");
if (!num2)
{
printf("Get num2 object failed");
return -1;
}
if (num2->type == cJSON_Object)
{
/* 對象 */
js_name = cJSON_GetObjectItem(num2, "name");
js_class = cJSON_GetObjectItem(num2, "class");
js_age = cJSON_GetObjectItem(num2, "age");
printf("\r\n num2(name:%s , class:%s, age:%d)\r\n", js_name->valuestring, js_class->valuestring, js_age->valueint);
}
cJSON_Delete(root);
return 0;
}
輸出結果:
3.3.4 數組解析
int cjson_test ( void )
{
cJSON * root,*js_lsit;
char * in_js_string = "{\"list\":[\"name\",\"class\"]}";
printf("\r\n--- CJSON Test %s---\r\n ",__TIME__);
root = cJSON_Parse(in_js_string);
if (!root)
{
printf("Get root object failed");
return -1;
}
js_lsit = cJSON_GetObjectItem(root, "list");
if (!js_lsit)
{
printf("Get js_lsit object failed");
return -1;
}
if(cJSON_Array == js_lsit->type) {
/* 獲取數組大小 */
int array_size = cJSON_GetArraySize(js_lsit);
cJSON * item;
// printf("list type is array: %d \r\n", js_lsit->type);
for (size_t i = 0; i < array_size; i++)
{
/* 解析數組成員 */
item = cJSON_GetArrayItem(js_lsit, i);
printf("item(%s) type is %d\r\n",item->valuestring, item->type);
}
}
if(root)
cJSON_Delete(root);
return 0;
}
輸出結果:
更深層次的嵌套思路一致的,在此不再贅述。