JSON|cJSON 先容以及详细项目编写

[复制链接]
发表于 2025-9-2 10:35:03 | 显示全部楼层 |阅读模式
一、JSON先容

JSON(JavaScript Object Notation 即JavaScript对象表现法)是一种轻量级的数据交换格式。采用完全独立于编程语言的文本格式来存储和表现数据。

  • JSON是一种数据交换格式.
  • JSON独立于编程语言(你不必学习JavaScript).
  • JSON表达数据的方式对通用的编程概念都很友好.
本文主要先容使用C语言分析JSON文件
二、JSON语法规则

数据结构


  • 对象(Object):使用花括号{}包裹,由键值对组成。键必须是字符串,使用双引号""包裹,键值对之间用逗号,分隔。
  • 数组(Array):使用方括号[]包裹,值之间用逗号,分隔。
值的类型


  • 字符串(String):使用双引号""包裹的 Unicode 字符序列。
  • 数值(Number):整数或浮点数,不使用引号,数字没有长度限定。
  • 布尔值(Boolean):true或false,表现"有"大概"没有",不使用引号。
  • 空值(Null):null,表现没有这项内容,它不是0,0是一个数字,本质上是在计数,不使用引号。
  • 嵌套对象或数组:可在值中嵌套对象或数组。数组:是由方括号括起来的一组值构成
留意事项


  • 不允许使用单引号。
  • 不允许包含注释。
  • 不允许使用未定义的变量或特殊符号(如NaN、Infinity)。
  • 数组和对象可以包含差异类型的值。
  • 使用冒号:来分割名称和值,名称始终在左侧,一定是字符串,值始终在右侧可以是多种类型
三、cJSON

3.1cJSON先容和获取

首先在GitHub上下载开源的cjson.h以及cjson.c文件
这里给出附件
使用的时候,只须要将这两个文件复制到工程目录,然后包含头文件cJSON.h即可,如下:
#include "cJSON.h"
3.2cJSON数据结构和设计思想

核心数据结构:cJSON 结构体

cJSON 使用单一结构体表现全部 JSON 类型,通过 type 字段区分差异类型。
cJSON.h中源码如下
  1. typedef struct cJSON {
  2.         struct cJSON *next,*prev;        /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
  3.         struct cJSON *child;                /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
  4.         int type;                                        /* The type of the item, as above. */
  5.         char *valuestring;                        /* The item's string, if type==cJSON_String */
  6.         int valueint;                                /* The item's number, if type==cJSON_Number */
  7.         double valuedouble;                        /* The item's number, if type==cJSON_Number */
  8.         char *string;                                /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
  9. } cJSON;
复制代码
同一表现全部 JSON 类型:将此中的一条JSON数据抽象出来,也就是一个键值对,用上面的结构体 strcut cJSON 来表现

  • 通过 type 字段(如 JSON_Object, JSON_Array, JSON_Number)区分差异类型。
  • 数值类型通过 valueint 和 valuedouble 存储,字符串类型通过 valuestring 存储
双向链表管理数组和对象成员

  • 数组(JSON_Array)和对象(JSON_Object)使用双向链表 next 和 prev 毗连成员。
  • 对象的键值对通过 child 指针指向第一个成员,每个成员的 string 字段存储键名
递归嵌套结构

  • 子节点可以是任意 JSON 类型,支持无穷层级嵌套(如数组包含对象,对象包含数组)。
设计思想

1.轻量级与可移植性


  • 无依赖:仅依赖标准 C 库(stdlib.h, string.h, math.h),无需额外依赖。
  • 单文件实现:核心代码会合在 cJSON.c 和 cJSON.h,便于集成到嵌入式体系或资源受限情况。
2. 简朴易用的 API

提供直观的函数接口,比方:

  • cJSON_Parse():分析 JSON 字符串为 cJSON 对象。
  • cJSON_GetObjectItem():通过键名获取对象成员。
  • cJSON_Print():将 cJSON 对象序列化为 JSON 字符串。
  • cJSON_CreateObject()/cJSON_CreateArray():创建 JSON 对象 / 数组。
3. 动态内存管理


  • 使用 malloc() 和 free() 动态分配和开释内存,需手动调用 cJSON_Delete() 开释不再使用的对象。
  • 支持自定义内存分配函数(通过 cJSON_Hooks 结构体),适配差异情况。
4. 错误处理


  • 分析失败时返回 NULL,通过 cJSON_GetErrorPtr() 获取错误位置。
  • 不提供详细错误信息(如行号、详细错误类型),得当快速分析简朴 JSON。
3.3cJSON数据封装(含源码分析)

封装JSON数据的过程,其实就是创建链表和向链表中添加节点的过程。
1.创建头指针
  1. cJSON* cjson_test = NULL;
复制代码
2.创建头结点,并将头指针指向头结点
  1. cjson_test = cJSON_CreateObject();
复制代码
3.调用函数向链表中插入结点
  1. cJSON_AddNullToObject(cJSON * const object, const char * const name);
  2. cJSON_AddTrueToObject(cJSON * const object, const char * const name);
  3. cJSON_AddFalseToObject(cJSON * const object, const char * const name);
  4. cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
  5. cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
  6. cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
  7. cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
  8. cJSON_AddObjectToObject(cJSON * const object, const char * const name);
  9. cJSON_AddArrayToObject(cJSON * const object, const char * const name);
复制代码
输出JSON数据
  1. (char *) cJSON_Print(const cJSON *item);
复制代码
一段完整的JSON数据就是一条长长的链表,cJSON提供了一个API,可以将整条链表中存放的JSON信息输出到一个字符串中,使用的时候,只须要吸收该函数返回的指针地址即可。
3.4cJSON数据分析(含源码分析)

数据分析调用cJSON_Parse(const char *value)
分析JSON数据的过程,其实就是剥离一个一个链表节点(键值对)的过程
  1. // 1.创建链表头指针:
  2. cJSON* cjson_test = NULL;
  3. //2.解析整段JSON数据,并将链表头结点地址返回,赋值给头指针:解析整段数据使用的API只有一个:
  4. (cJSON *) cJSON_Parse(const char *value);
  5. //3.根据键值对的名称从链表中取出对应的值,返回该键值对(链表节点)的地址
  6. (cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
  7. //4.如果JSON数据的值是数组,使用下面的两个API提取数据:
  8. (int) cJSON_GetArraySize(const cJSON *array);
  9. (cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
复制代码
3.5cJSON内存管理

cJSON的全部利用都是基于链表的,cJSON在使用过程中大量的使用malloc从堆中分配动态内存的,所以在使用完之后,应当及时调用下面的函数,清空cJSON指针所指向的内存,该函数也可用于删除某一条数据:
  1. void cJSON_Delete(cJSON *item);
复制代码
3.6给出代码示例封装及分析

嵌套的数据封装时递归封装分析时也递归分析
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include "cJSON.h"
  5. // 封装(生成)JSON 数据
  6. char* encapsulate_json_data() {
  7.     // 创建根对象
  8.     cJSON *root = cJSON_CreateObject();
  9.    
  10.     // 创建 user 对象
  11.     cJSON *user = cJSON_CreateObject();
  12.     cJSON_AddNumberToObject(user, "id", 12345);
  13.     cJSON_AddStringToObject(user, "name", "Alice");
  14.    
  15.     // 创建 contact 对象
  16.     cJSON *contact = cJSON_CreateObject();
  17.     cJSON_AddStringToObject(contact, "email", "alice@example.com");
  18.    
  19.     // 创建 phone 数组
  20.     cJSON *phone = cJSON_CreateArray();
  21.     cJSON_AddItemToArray(phone, cJSON_CreateString("123-456-7890"));
  22.     cJSON_AddItemToArray(phone, cJSON_CreateString("098-765-4321"));
  23.     cJSON_AddItemToObject(contact, "phone", phone);
  24.    
  25.     // 将 contact 添加到 user
  26.     cJSON_AddItemToObject(user, "contact", contact);
  27.    
  28.     // 将 user 添加到根对象
  29.     cJSON_AddItemToObject(root, "user", user);
  30.    
  31.     // 添加布尔值
  32.     cJSON_AddBoolToObject(root, "isActive", 1);  // 1 表示 true
  33.    
  34.     // 添加嵌套数组
  35.     cJSON *nested_array = cJSON_CreateArray();
  36.     cJSON_AddItemToArray(nested_array, cJSON_CreateNumber(100));
  37.     cJSON_AddItemToArray(nested_array, cJSON_CreateString("nested value"));
  38.    
  39.     cJSON *nested_obj = cJSON_CreateObject();
  40.     cJSON_AddStringToObject(nested_obj, "key", "value");
  41.     cJSON_AddItemToArray(nested_array, nested_obj);
  42.    
  43.     cJSON_AddItemToObject(root, "nested", nested_array);
  44.    
  45.     // 生成 JSON 字符串
  46.     char *json_str = cJSON_Print(root);
  47.    
  48.     // 释放 JSON 对象
  49.     cJSON_Delete(root);
  50.    
  51.     return json_str;
  52. }
  53. // 解析 JSON 数据
  54. void parse_json_data(const char *json_str) {
  55.     // 解析 JSON 字符串
  56.     cJSON *root = cJSON_Parse(json_str);
  57.     if (root == NULL) {
  58.         const char *error_ptr = cJSON_GetErrorPtr();
  59.         if (error_ptr != NULL) {
  60.             fprintf(stderr, "Error before: %s\n", error_ptr);
  61.         }
  62.         return;
  63.     }
  64.    
  65.     // 提取基本字段
  66.     cJSON *isActive = cJSON_GetObjectItem(root, "isActive");
  67.     if (cJSON_IsBool(isActive)) {
  68.         printf("isActive: %s\n", isActive->valueint ? "true" : "false");
  69.     }
  70.    
  71.     // 提取 user 对象
  72.     cJSON *user = cJSON_GetObjectItem(root, "user");
  73.     if (cJSON_IsObject(user)) {
  74.         cJSON *id = cJSON_GetObjectItem(user, "id");
  75.         cJSON *name = cJSON_GetObjectItem(user, "name");
  76.         
  77.         if (cJSON_IsNumber(id) && cJSON_IsString(name)) {
  78.             printf("User ID: %d\n", id->valueint);
  79.             printf("User Name: %s\n", name->valuestring);
  80.         }
  81.         
  82.         // 提取 contact 对象
  83.         cJSON *contact = cJSON_GetObjectItem(user, "contact");
  84.         if (cJSON_IsObject(contact)) {
  85.             cJSON *email = cJSON_GetObjectItem(contact, "email");
  86.             if (cJSON_IsString(email)) {
  87.                 printf("Email: %s\n", email->valuestring);
  88.             }
  89.             
  90.             // 提取 phone 数组
  91.             cJSON *phone = cJSON_GetObjectItem(contact, "phone");
  92.             if (cJSON_IsArray(phone)) {
  93.                 int array_size = cJSON_GetArraySize(phone);
  94.                 printf("Phone Numbers (%d):\n", array_size);
  95.                 for (int i = 0; i < array_size; i++) {
  96.                     cJSON *phone_item = cJSON_GetArrayItem(phone, i);
  97.                     if (cJSON_IsString(phone_item)) {
  98.                         printf("  %d: %s\n", i+1, phone_item->valuestring);
  99.                     }
  100.                 }
  101.             }
  102.         }
  103.     }
  104.    
  105.     // 解析嵌套数组
  106.     cJSON *nested = cJSON_GetObjectItem(root, "nested");
  107.     if (cJSON_IsArray(nested)) {
  108.         printf("Nested Array:\n");
  109.         int array_size = cJSON_GetArraySize(nested);
  110.         for (int i = 0; i < array_size; i++) {
  111.             cJSON *item = cJSON_GetArrayItem(nested, i);
  112.             printf("  Item %d: ", i+1);
  113.             
  114.             if (cJSON_IsNumber(item)) {
  115.                 printf("Number = %f\n", item->valuedouble);
  116.             } else if (cJSON_IsString(item)) {
  117.                 printf("String = %s\n", item->valuestring);
  118.             } else if (cJSON_IsObject(item)) {
  119.                 printf("Object\n");
  120.                 cJSON *key = cJSON_GetObjectItem(item, "key");
  121.                 if (cJSON_IsString(key)) {
  122.                     printf("    key = %s\n", key->valuestring);
  123.                 }
  124.             }
  125.         }
  126.     }
  127.    
  128.     // 释放 JSON 对象
  129.     cJSON_Delete(root);
  130. }
  131. int main() {
  132.     // 封装 JSON 数据
  133.     char *json_str = encapsulate_json_data();
  134.     if (json_str == NULL) {
  135.         fprintf(stderr, "Failed to generate JSON data.\n");
  136.         return 1;
  137.     }
  138.    
  139.     // 打印生成的 JSON
  140.     printf("Generated JSON:\n%s\n\n", json_str);
  141.    
  142.     // 解析 JSON 数据
  143.     printf("Parsing JSON data...\n");
  144.     parse_json_data(json_str);
  145.    
  146.     // 释放 JSON 字符串
  147.     free(json_str);
  148.    
  149.     return 0;
  150. }
复制代码
四、cJSON项目

这个项目实现了一个使用 C 语言分析 JSON 文件的应用步伐,主要内容是从包含中国行政区划信息的 JSON 文件中提取特定省份的都会列表。
详细实现思绪

由于cJSON.h中并没有定义对文件的利用,所以通过使用标准 C 库函数 fopen、fread 和 fseek 读取 JSON 文件内容,把文件内容读取到字符串中,然后分析字符串,留意使用动态分配内存存储 JSON 数据,并在使用后准确开释,实际利用直接使用 cJSON 库分析 JSON 数据结构,包罗对象、数组的遍历,最后通过使用 strcmp 举行字符串比力,即可定位目标省份。
代码:
  1. #define _CRT_SECURE_NO_WARNINGS
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include "cJSON.h"
  6. char* read_file(const char* filename) {
  7.         FILE* f = fopen(filename, "rb");
  8.         if (!f) {
  9.                 perror("无法打开文件");
  10.                 return NULL;
  11.         }
  12.         fseek(f, 0, SEEK_END);
  13.         long size = ftell(f);
  14.         fseek(f, 0, SEEK_SET);
  15.         char* buff = (char*)malloc(size + 1);
  16.         if (buff == NULL) {
  17.                 perror("创建内存失败");
  18.                 fclose(f);
  19.                 return NULL;
  20.         }
  21.         size_t bytes_read = fread(buff, 1, size, f);//目标缓冲区 buff,每个数据项的大小(这里是 1 字节),要读取的数据项数量 size,以及文件流 f。
  22.         if (bytes_read < size) {
  23.                 if (feof(f)) {
  24.                         //feof 函数用于检查文件流 f 是否已经到达文件末尾。
  25.                         printf("警告:文件已到达末尾,读取 %zu 字节(期望 %ld 字节)\n", bytes_read, size);
  26.                 }
  27.                 else if (ferror(f)) {
  28.                         perror("读取错误");
  29.                         free(buff);
  30.                         fclose(f);
  31.                         return NULL;
  32.                 }
  33.         }
  34.         buff[bytes_read] = '\0';
  35.         fclose(f);
  36.         return buff;
  37. }
  38. int main() {
  39.         char* json_str = read_file("city.json");
  40.         if (json_str == NULL) {
  41.                 return -1;
  42.         }
  43.         cJSON* root = cJSON_Parse(json_str);
  44.         if (root == NULL) {
  45.                 const char* error_ptr = cJSON_GetErrorPtr();
  46.                 if (error_ptr != NULL) {
  47.                         fprintf(stderr, "JSON解析错误: %s\n", error_ptr);
  48.                 }
  49.                 free(json_str);
  50.                 return -1;
  51.         }
  52.         // 检查 root 是否为数组
  53.         if (root->type != cJSON_Array) {
  54.                 printf("JSON 根节点不是数组类型\n");
  55.                 cJSON_Delete(root);
  56.                 free(json_str);
  57.                 return -1;
  58.         }
  59.         const char* target_province = "陕西";
  60.         int province_found = 0;
  61.         // 遍历所有省份
  62.         int province_count = cJSON_GetArraySize(root);
  63.         for (int i = 0; i < province_count; i++) {
  64.                 cJSON* province_item = cJSON_GetArrayItem(root, i);
  65.                 if (!province_item) continue;
  66.                 // 获取省份名称
  67.                 cJSON* name = cJSON_GetObjectItem(province_item, "label");
  68.                 if (!name || name->type != cJSON_String) continue;
  69.                 // 检查是否为目标省份
  70.                 if (strcmp(name->valuestring, target_province) == 0) {
  71.                         printf("找到省份:%s\n", name->valuestring);
  72.                         province_found = 1;
  73.                         // 获取城市数组
  74.                         cJSON* cities_array = cJSON_GetObjectItem(province_item, "children");//注意数据嵌套关系,城市在省份的children数组里
  75.                         if (cities_array && cities_array->type == cJSON_Array) {
  76.                                 int city_count = cJSON_GetArraySize(cities_array);
  77.                                 printf("包含 %d 个城市:\n", city_count);
  78.                                 for (int j = 0; j < city_count; j++) {
  79.                                         cJSON* city_item = cJSON_GetArrayItem(cities_array, j);
  80.                                         if (!city_item) continue;
  81.                                         cJSON* city_name = cJSON_GetObjectItem(city_item, "label");
  82.                                         if (city_name && city_name->type == cJSON_String) {
  83.                                                 printf("  %d. %s\n", j + 1, city_name->valuestring);
  84.                                         }
  85.                                 }
  86.                         }
  87.                         else {
  88.                                 printf("未找到该省份的城市数据\n");
  89.                         }
  90.                         break; // 找到目标省份后退出循环
  91.                 }
  92.         }
  93.         if (!province_found) {
  94.                 printf("未找到省份:%s\n", target_province);
  95.         }
  96.         cJSON_Delete(root);
  97.         free(json_str);
  98.         return 0;
  99. }
复制代码
运行效果:

这里以查找陕西省的都会为例:


五、 JSON的应用场景有哪些?

多个应用场景:


  •         Web API 数据传输

    • RESTful API 的请求和响应格式
    • 前后端数据交互(如 AJAX 请求)
           
  •         配置文件

    • 应用步伐配置(如 Node.js 的 package.json)
    • 开发工具配置(如 ESLint、Babel)
           
  •         数据存储

    • NoSQL 数据库(如 MongoDB)存储文档
    • 临时数据缓存(如 Redis)
           
  •         跨语言数据交换

    • 微服务间通信(如通过消息队列)
    • 差异体系间数据集成
           
  •         移动应用开发

    • 服务器交换数据(如 JSON API)
    • 本地存储(如 React Native 的 AsyncStorage)
           
  •         日志日志监控监控体系
           
  •         前端框架

    • React/Vue 组件属性转达
    • 路由配置
           
优势总结:


  • 轻量级,易于阅读和编写
  • 支持嵌套结构,得当复杂数据
  • 几乎全部编程语言都有分析库
  • 与 JavaScript 原生兼容
  • 比 XML 更简洁,分析服从更高
尾语:项目学习并完成于2025.5.10,假如觉得有帮助有收获可以点赞收藏,会持续更新输出有效的内容,感兴趣可以关注我!


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
继续阅读请点击广告

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

×
登录参与点评抽奖,加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表