C語言使用cJson庫解析json

cjson庫

在github或gitee上cjson,使用庫中的cJSON.ccJSON.h即可。
以下是我使用cjson的案例僅供參考。
點擊下載 我的cJosn使用案例

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 */
/* 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; // 孩子節點,當爲數組或對象類型纔有值,基礎類型爲NULL

   /* 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的幾個常用接口

json字符串的解析,使用完,記得釋放空間

// 傳入json字符串,將字符串轉換爲cJSON對象,這時在堆中申請了空間,
// cJSON對象用完,需要使用cJSON_Delete(cJSON *c)函數釋放空間.
cJSON * cJSON_Parse(const char *value);

cjson對象空間的釋放

// 釋放通過cJSON_Parse,cJSON_CreateObject等create函數申請的空間
void cJSON_Delete(cJSON *c);

判斷cjson對象的類型,有以下幾種類型

/* These functions check the type of an item  用於判斷cJSON對象類型*/
CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);

創建cjson對象,使用記得釋放空間

/* 創建cjson對象These calls create a cJSON item of the appropriate type. */
CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
/* raw json */
CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);

將創建或修改的cjson對象輸出爲字符串,然後自己保存到文件,用完字符串需要釋放空間

/* Render a cJSON entity to text for transfer/storage. */
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);

申請與釋放空間,cJSON_Print返回的字符串空間,可用cJSON_free釋放。

CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
CJSON_PUBLIC(void) cJSON_free(void *object);

對cjson庫進一步封裝

對cjson庫封裝了幾個函數,方便對json進行操作,以供參考

  1. 根據json文件路徑,將文件轉爲json對象,失敗返回NULL

    cJSON * json_parse(const char *path);
    
  2. 在父對象中,根據鍵值對中的名字,獲取對應的子對象,失敗返回NULL

    //pjson爲父cjson對象, pitemname爲子項名,返回子對象
    const cJSON * json_get_object(const cJSON *pjson, const char *pitemname);
    
  3. 在父對象中,根據鍵值對中的名字,獲取對應的字符串

    //pjson爲父cjson對象, pitemname爲子項名,pdft爲默認值,返回鍵值對中的值,找不到返回默認值
    const char * json_get_string(const cJSON *pjson,  const char *pitemname, const char *pdft);
    
  4. 在父對象中,根據鍵值對中的名字,獲取對應的數值

    //pjson爲父cjson對象, pitemname爲子項名,pdft爲默認值,返回鍵值對中的值,找不到返回默認值
    int json_get_number(const cJSON *pjson,  const char *pitemname, int pdft);
    
  5. 在父對象中,獲取類型爲數組的子對象,數組成員個數

    int json_get_array_size(const cJSON *pjson, const char *pitemname)
    

創建common.c的代碼文件,並創建common.h的頭文件,
common.c具體封裝代碼如下

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"

#define in_range(c, lo, up)  ((unsigned char)c >= lo && (unsigned char)c <= up)
#define isdigit(c)           in_range(c, '0', '9')

/**
 ******************************************************************************
 * @brief   字符串轉換爲正整數
 * @param[in] : *pstr : 字符串
 * @retval  >= 0 : 數值
 * @retval  <  0 : 轉換失敗
 ******************************************************************************
 */
int
str2i(const char *pstr)
{
    int ret = 0;
    if (!(*pstr && isdigit(*pstr)))
        return -1;
    do
    {
        ret = ret * 10 + (*pstr - '0');
        pstr++;
    } while (*pstr && isdigit(*pstr));
    return ret;
}

/**
 ******************************************************************************
 * @brief   將json文件解析爲json 對象
 * @param[in]  *path : 文件路徑
 * @retval  cJSON *  : 爲NULL表示解析失敗
 ******************************************************************************
 */
cJSON *
json_parse(const char *path)
{
    int file_size = 0;
    FILE  *fp = NULL;
    char *pstr = NULL;
    cJSON *pjson = NULL;
    int ret = 0;

    /*
     * 1. 獲取文件長度
     * 2. 申請內存, 讀取文件
     * 3. 調用cjson庫解析結構
     * 4. 釋放內存
     */
    do
    {
        if (!path)
        {
            break;
        }
        fp = fopen(path, "rb");
        if (NULL == fp)
        {
            break;
        }

		fseek(fp, 0, SEEK_END);
        file_size = ftell(fp);
        fseek(fp, 0, SEEK_SET);
        pstr = malloc(file_size + 1);
        if (!pstr)
        {
            break;
        }
        pstr[file_size] = 0; //last = 1
        if (file_size != (int)fread(pstr, 1, file_size, fp))
        {
            break;
        }
		cJSON_Print(pjson);
        pjson = cJSON_Parse(pstr);
        ret = pjson ? 1 : 0;
    } while (0);

    if (fp)
    {
        fclose(fp);
    }
    if (pstr)
    {
        free(pstr);
    }
    return ret ? pjson : NULL;
}

/**
 ******************************************************************************
 * @brief   獲取json數組成員對象
 * @param[in]  *pjson : json對象
 * @param[in]  *pitemname : 成員
 * @retval   數值
 ******************************************************************************
 */
const cJSON *
json_get_object(const cJSON *pjson,
        const char *pitemname)
{
    /*
     * 1. 父子使用點(.)連接
     * 2. 需要解析array, 方括號[%d]
     */
    int pos;
    char item[64];
    char *ptmp;
    char *ptr = NULL;
    const char *pstr;
    const cJSON *parray;
    const cJSON *pobject = pjson;

    if (!pjson || !pitemname || !pitemname[0] || (strlen(pitemname) >= sizeof(item)))
    {
        return NULL;
    }

    strncpy(item, pitemname, sizeof(item));
    char* str = item;

    //使用"."分離
    while (pobject && (pstr = strtok_r(str, ".", &ptr)))
    {
        str = NULL;
        if ((ptmp = strstr(pstr, "["))) //Array
        {
            if (0 > (pos = str2i(ptmp + 1)))
            {
                break;
            }
            ptmp[0] = 0;
            parray = cJSON_GetObjectItem(pobject, pstr);
            if (!cJSON_IsArray(parray) || (pos >= cJSON_GetArraySize(parray)))
            {
                break;
            }
            pobject = cJSON_GetArrayItem(parray, pos);
        }
        else //object
        {
            pobject = cJSON_GetObjectItem(pobject, pstr);
        }
    }
    return pobject;
}

/**
 ******************************************************************************
 * @brief   獲取json字符串
 * @param[in]  *pjson : json對象
 * @param[in]  *pitemname : 成員
 * @param[in]  *pdft  : 默認值
 * @retval   字符串(確保非空)
 ******************************************************************************
 */
const char *
json_get_string(const cJSON *pjson,
        const char *pitemname,
        const char *pdft)
{
    const char *pstr;
    const cJSON *pobject;
    do
    {
        if (!pjson || !pitemname || !pitemname[0])
        {
            break;
        }
        if (!(pobject = json_get_object(pjson, pitemname)))
        {
            break;
        }
        pstr = cJSON_GetStringValue(pobject);
        return pstr ? pstr : "";
    } while (0);
    return pdft ? pdft : "";
}

/**
 ******************************************************************************
 * @brief   獲取json數值
 * @param[in]  *pjson : json對象
 * @param[in]  *pitemname : 成員
 * @param[in]  dft    : 默認值
 * @retval   數值
 ******************************************************************************
 */
int
json_get_number(const cJSON *pjson,
        const char *pitemname,
        int dft)
{
    const cJSON *pobject;
    do
    {
        if (!pjson || !pitemname || !pitemname[0])
        {
            break;
        }
        if (!(pobject = json_get_object(pjson, pitemname)))
        {
            break;
        }
        if (!cJSON_IsNumber(pobject))
        {
            break;
        }
        return pobject->valueint;
    } while (0);
    return dft;
}

/**
 ******************************************************************************
 * @brief   獲取json數組成員數
 * @param[in]  *pjson : json對象
 * @param[in]  *pitemname : 成員
 * @retval   數值
 ******************************************************************************
 */
int
json_get_array_size(const cJSON *pjson,
        const char *pitemname)
{
    const cJSON *pobject;

    do
    {
        if (!pjson || !pitemname || !pitemname[0])
        {
            break;
        }
        if (!(pobject = json_get_object(pjson, pitemname)))
        {
            break;
        }
        if (!cJSON_IsArray(pobject))
        {
            break;
        }
        return cJSON_GetArraySize(pobject);
    } while (0);

    return 0;
}

common.h文件

#ifndef _COMMON_H
#define _COMMON_H

#include "./json/cJSON.h"

#if defined(__cplusplus)
extern "C"
{         
#endif

extern const cJSON *
json_get_object(const cJSON *pjson,
        const char *pitemname);

extern const char *
json_get_string(const cJSON *pjson,
        const char *pitemname,
        const char *pdft);

extern int
json_get_number(const cJSON *pjson,
        const char *pitemname,
        int dft);

extern int
json_get_array_size(const cJSON *pjson,
        const char *pitemname);

extern cJSON *
json_parse(const char *path);
#if defined(__cplusplus)
}
#endif
#endif

調用測試案例

創建config.json文件,文件內容如下

{
    "multicastIp": "239.0.0.10",
    "multicastPort": 8805,
    "services": [
        "192.168.0.118",
        "172.19.75.111",
        "172.19.75.45",
        "172.19.75.234"
    ]
}

創建mian.c的測試文件

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"
#define CONFIG_FILE "./config.json"


typedef struct{
    int muticast_port;    //心跳端口號
    char * muticast_ip;   //組播IP
    int service_count;    // 服務器數量
    char**  service_ips;  // 服務器ip地址
} config_info_t;

config_info_t config;   //配置文件信息,只供讀取

/**
 ******************************************************************************
 * @brief      初始化配置
 * @retval      0: 成功, -1: 失敗
 ******************************************************************************
 */
int config_init()
{
    char key[64];
    cJSON * obj = NULL;

    // 檢查配置文件並獲取json對象
    if (NULL == (obj = json_parse(CONFIG_FILE)))
    {
		exit(1);
    }
    // 獲取json中的數據
    config.muticast_port = json_get_number(obj, "multicastPort", 8805);
    config.muticast_ip = strdup(json_get_string(obj, "multicastIp", "239.0.0.10"));
    config.service_count = json_get_array_size(obj, "services");// 獲取服務器個數

    // 根據服務器個數, 申請空間
    if (NULL == (config.service_ips = (char**) malloc(sizeof(char*) * config.service_count)))
    {
        cJSON_Delete(obj); //釋放空間
        return -1;
    }
 
    // 獲取所有服務器的地址
    memset(key, 0, sizeof(key));
    for (int i = 0; i < config.service_count; ++i)
    {
        snprintf(key, sizeof(key), "services[%d]", i);
        config.service_ips[i] = strdup(json_get_string(obj, key, NULL));
    }

    cJSON_Delete(obj); //釋放json對象空間
    return 0;
}

/**
 ******************************************************************************
 * @brief       銷燬配置對象, 釋放空間
 * @param[in]  * pconfig : 配置文件對象
 ******************************************************************************
 */
void config_destroy()
{
    if (config.muticast_ip)
    {
        free(config.muticast_ip);
        config.muticast_ip = NULL;
    }

    if (config.service_ips)
    {
        for (int i = 0; i < config.service_count; ++i)
        {
            if (config.service_ips[i])
            {
                free(config.service_ips[i]);
                config.service_ips[i] = NULL;
            }
        }
        free(config.service_ips);
        config.service_ips = NULL;
    }
}

int main(int argc, char * argv[])
{
    config_init();

    printf("組播端口號:\t %d\n", config.muticast_port);
    printf("組播 IP:\t %s\n", config.muticast_ip);
    printf("服務端個數:\t %d\n", config.service_count);
    for (int i = 0; i < config.service_count; i++)
    {
        printf("服務器%d:\t %s\n", i, config.service_ips[i]);
    }
    
    config_destroy();
}

測試結果如下

lmz@lmz-X280:~/桌面/code/c/test/cJsonTest$ make all
gcc -fPIC -c -o common.o common.c 
gcc -fPIC -c -o cJSON.o ./json/cJSON.c
gcc main.c -o main common.o cJSON.o
lmz@lmz-X280:~/桌面/code/c/test/cJsonTest$ ./main 
組播端口號:      8805
組播 IP:         239.0.0.10
服務端個數:      4
服務器0:         192.168.0.118
服務器1:         172.19.75.111
服務器2:         172.19.75.45
服務器3:         172.19.75.234

補充

看到iperf中有個函數對cJSON進行了個封裝,覺得挺好的,可以加入common.c中,方便對cJSON更好的操作。
需要#include <stdarg.h>,其功能是使用printf的形式創建cJSON對象。


/* Helper routine for building cJSON objects in a printf-like manner.
**
** Sample call:
**   j = json_printf("foo: %b  bar: %d  bletch: %f  eep: %s", b, i, f, s);
**
** The four formatting characters and the types they expect are:
**   %b  boolean           int
**   %d  integer           int64_t
**   %f  floating point    double
**   %s  string            char *
** If the values you're passing in are not these exact types, you must
** cast them, there is no automatic type coercion/widening here.
**
** The colons mark the end of field names, and blanks are ignored.
**
** This routine is not particularly robust, but it's not part of the API,
** it's just for internal iperf3 use.
*/
cJSON*
json_printf(const char *format, ...)
{
    cJSON* o;
    va_list argp;
    const char *cp;
    char name[100];
    char* np;
    cJSON* j;

    o = cJSON_CreateObject();
    if (o == NULL)
        return NULL;
    va_start(argp, format);
    np = name;
    for (cp = format; *cp != '\0'; ++cp) {
	switch (*cp) {
	    case ' ':
	    break;
	    case ':':
	    *np = '\0';
	    break;
	    case '%':
	    ++cp;
	    switch (*cp) {
		case 'b':
		j = cJSON_CreateBool(va_arg(argp, int));
		break;
		case 'd':
		j = cJSON_CreateNumber(va_arg(argp, int64_t));
		break;
		case 'f':
		j = cJSON_CreateNumber(va_arg(argp, double));
		break;
		case 's':
		j = cJSON_CreateString(va_arg(argp, char *));
        break;
		default:
		va_end(argp);
		return NULL;
	    }
	    if (j == NULL) {
	    	va_end(argp);
	    	return NULL;
	    }
	    cJSON_AddItemToObject(o, name, j);
	    np = name;
	    break;
	    default:
	    *np++ = *cp;
	    break;
	}
    }
    va_end(argp);
    return o;
}

cJSON* json_printf(const char *format, ...);使用示例


int main(int argc, char * argv[])
{
    char ip[] = "127.0.0.1";
    int port = 8080;

    cJSON * cjobj = cJSON_CreateObject();
    /*printf的形式創建對象*/
    cJSON * cjip = json_printf("ip: %s port: %d", ip, port);
    cJSON_AddItemToObject(cjobj, "service", cjip);
    
    char * p = cJSON_Print(cjobj);

    printf("%s \n", p);
    cJSON_free(p);
    cJSON_Delete(cjobj);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章