当前位置: 首页 > 工具软件 > json.c > 使用案例 >

C语言使用cJson库解析json

林修雅
2023-12-01

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);
}
 类似资料: