解码JSON
JSON 核心概念
JSON(JavaScript Object Notation)是 “JavaScript 对象表示法” 的简称,是一种轻量级数据交换文本格式,不依赖任何编程语言。它具有简洁清晰的层次结构,易于人类阅读编写,同时便于机器解析和生成,现已成为 Web 开发、跨平台应用间数据传输的主流格式。
JSON 数据格式
JSON 的核心数据结构只有两种,可相互嵌套组成复杂数据:
对象(无序键值对集合)
- 用花括号
{}包围,键值对以:分隔,多个键值对用,分隔。 - 键必须是字符串(用双引号
""包裹),值可是任意合法 JSON 类型。 - 示例(修正原文档键名重复错误):
{
"name": "lmx", // 值类型:字符串
"age": 29, // 值类型:整型
"score": [ // 值类型:数组(元素为对象)
{
"math": 85.5, // 值类型:浮点型
"english": 92 // 值类型:整型
}
]
}
数组(有序值集合)
- 用方括号
[]包围,多个值用,分隔。 - 数组中的值可是任意合法 JSON 类型,包括对象和数组。
- 示例:
["apple", "banana", 100, true, null, {"city": "Beijing"}]
合法值类型
- 字符串(双引号包裹,不可用单引号)
- 数值(整型、浮点型,无引号)
- 布尔值(
true或false,小写) - 空值(
null,小写) - 对象(
{}包裹的键值对集合) - 数组(
[]包裹的值集合)
cJSON 库介绍
cJSON 是基于 C 语言的轻量级 JSON 解析 / 构造库,开源且跨平台(兼容 C89 标准),适用于嵌入式开发等场景。
获取与使用
- 下载地址:https://github.com/DaveGamble/cJSON
- 核心文件:仅需cJSON.c(实现文件)和cJSON.h(头文件),直接复制到项目中即可使用。
- 编译方式:将
cJSON.c与项目源码一起编译(如gcc main.c cJSON.c -o main)。
核心结构体(cJSON)
用于存储 JSON 数据的树形结构节点,每个节点对应一个 JSON 值:
/**
* cJSON核心结构体,存储单个JSON节点数据
* @brief 构成JSON树形结构的基本单元,每个节点对应一个JSON值(字符串、数字、对象等)
* @param next 链表下一个节点指针(用于遍历数组/对象的子节点)
* @param prev 链表上一个节点指针(用于双向遍历)
* @param child 子节点指针(对象/数组类型节点的子元素链表头)
* @param type 节点类型(如cJSON_String、cJSON_Number、cJSON_Object等)
* @param valuestring 字符串类型值(仅type为cJSON_String或cJSON_Raw时有效)
* @param valueint 整型值(type为cJSON_Number时有效,建议用cJSON_SetNumberValue设置)
* @param valuedouble 浮点型值(type为cJSON_Number时有效)
* @param string 节点名称(仅当节点是对象的子节点时有效,即键名)
*/
typedef struct cJSON {
struct cJSON *next;
struct cJSON *prev;
struct cJSON *child;
int type;
char *valuestring;
int valueint;
double valuedouble;
char *string;
} cJSON;
JSON 解析流程(cJSON 实现)
解析流程:获取 JSON 字符串 → 解析为 cJSON 树形结构 → 提取数据 → 释放资源
关键函数
解析 JSON 字符串
/**
* 解析JSON格式字符串为cJSON树形结构
* @brief 将零终止的JSON字符串转换为cJSON结构体指针,后续可通过该指针遍历提取数据
* @param string 输入的JSON格式字符串(必须零终止,格式合法)
* @return cJSON* 成功返回指向cJSON树形结构根节点的指针,失败返回NULL
* @note 解析成功后需调用cJSON_Delete释放内存,避免内存泄漏
* 若JSON字符串格式错误(如括号不匹配、键未用双引号),返回NULL
*/
cJSON *cJSON_Parse(const char *string);
调试输出 JSON
/**
* 将cJSON树形结构转换为格式化的JSON字符串
* @brief 用于调试验证解析结果,生成易读的JSON字符串
* @param json 指向cJSON树形结构根节点的指针
* @return char* 成功返回动态分配的JSON字符串,失败返回NULL
* @note 返回的字符串需手动用free释放,否则会造成内存泄漏
* 生成的字符串包含换行和缩进,便于人类阅读
*/
char *cJSON_Print(const cJSON *json);
获取对象中的键值对
/**
* 从JSON对象中获取指定键名的子节点(不区分大小写)
* @brief 用于提取对象类型节点中某个键对应的值节点
* @param object 指向JSON对象节点的指针(type必须为cJSON_Object)
* @param string 要查找的键名(字符串)
* @return cJSON* 成功返回对应值节点的指针,失败返回NULL
* @note 若对象中无该键,或传入的节点不是对象类型,返回NULL
* 区分大小写版本可用cJSON_GetObjectItemCaseSensitive
*/
cJSON *cJSON_GetObjectItem(const cJSON *const object, const char *const string);
获取数组信息
/**
* 获取JSON数组的元素个数
* @brief 用于确定数组节点中包含的子元素数量
* @param array 指向JSON数组节点的指针(type必须为cJSON_Array)
* @return int 成功返回数组元素个数,失败返回0
* @note 若传入的节点不是数组类型,返回0
*/
int cJSON_GetArraySize(const cJSON *array);
/**
* 获取数组中指定索引的元素节点
* @brief 按索引遍历数组,提取对应位置的子节点
* @param array 指向JSON数组节点的指针(type必须为cJSON_Array)
* @param index 元素索引(从0开始)
* @return cJSON* 成功返回对应索引的元素节点指针,失败返回NULL
* @note 索引超出数组范围(>=元素个数)时返回NULL
*/
cJSON *cJSON_GetArrayItem(const cJSON *array, int index);
释放资源
/**
* 释放cJSON树形结构占用的内存
* @brief 递归释放整个cJSON链表的内存,包括所有子节点
* @param item 指向cJSON树形结构根节点的指针
* @return void 无返回值
* @note 必须在解析完成后调用,否则会造成严重内存泄漏
* 仅需传入根节点指针,会自动递归释放所有子节点
*/
void cJSON_Delete(cJSON *item);
完整解析示例(解析天气 API 响应)
#include <stdio.h>
#include <stdlib.h>
#include "cJSON.h"
int main() {
// 假设这是从API获取的JSON字符串
const char *json_str = "{"
"\"results\": ["
"{"
"\"path\": \"Beijing, Beijing, China\","
"\"timezone\": \"Asia/Shanghai\","
"\"timezone_offset\": \"+08:00\","
"\"txt\": \"sunny\","
"\"code\": \"0\","
"\"temperature\": \"32\","
"\"last_update\": \"2024-06-11T11:27:51+08:00\""
"},"
"{"
"\"path\": \"Shanghai, Shanghai, China\","
"\"timezone\": \"Asia/Shanghai\","
"\"timezone_offset\": \"+08:00\","
"\"txt\": \"cloudy\","
"\"code\": \"1\","
"\"temperature\": \"28\","
"\"last_update\": \"2024-06-11T11:27:51+08:00\""
"}"
"]"
"}";
// 解析JSON
cJSON *root = cJSON_Parse(json_str);
if (root == NULL) {
printf("JSON解析失败:%s\n", cJSON_GetErrorPtr());
return -1;
}
// 获取results数组
cJSON *results_array = cJSON_GetObjectItem(root, "results");
if (results_array == NULL || !cJSON_IsArray(results_array)) {
printf("未找到results数组\n");
cJSON_Delete(root);
return -1;
}
// 使用 cJSON_ArrayForEach 宏遍历数组
// 宏的参数:当前元素节点指针, 数组节点指针
cJSON *weather_obj;
cJSON_ArrayForEach(weather_obj, results_array) {
// 提取基础类型值
cJSON *city = cJSON_GetObjectItem(weather_obj, "path");
cJSON *weather = cJSON_GetObjectItem(weather_obj, "txt");
cJSON *temp = cJSON_GetObjectItem(weather_obj, "temperature");
cJSON *update_time = cJSON_GetObjectItem(weather_obj, "last_update");
// 输出提取的信息
if (city && cJSON_IsString(city) && weather && cJSON_IsString(weather) &&
temp && cJSON_IsString(temp) && update_time && cJSON_IsString(update_time)) {
printf("\n城市:%s\n", city->valuestring);
printf("天气:%s\n", weather->valuestring);
printf("温度:%s℃\n", temp->valuestring);
printf("最后更新时间:%s\n", update_time->valuestring);
}
}
// 释放资源
cJSON_Delete(root);
return 0;
}
JSON 构造流程(cJSON 实现)
构造流程:创建根对象 → 添加子节点 / 键值对 → 生成 JSON 字符串 → 释放资源
关键构造函数
/**
* 创建JSON对象节点(对应{})
* @brief 生成一个空的JSON对象节点,作为树形结构的根或子对象
* @return cJSON* 成功返回对象节点指针,失败返回NULL
* @note 需配合cJSON_AddItemToObject等函数添加键值对
*/
cJSON *cJSON_CreateObject(void);
/**
* 创建JSON数组节点(对应[])
* @brief 生成一个空的JSON数组节点
* @return cJSON* 成功返回数组节点指针,失败返回NULL
* @note 需配合cJSON_AddItemToArray等函数添加元素
*/
cJSON *cJSON_CreateArray(void);
/**
* 创建字符串类型节点
* @brief 生成存储指定字符串的JSON节点
* @param string 要存储的字符串(零终止)
* @return cJSON* 成功返回字符串节点指针,失败返回NULL
*/
cJSON *cJSON_CreateString(const char *string);
/**
* 创建数值类型节点
* @brief 生成存储指定浮点型数值的JSON节点(支持整型和浮点型)
* @param num 要存储的数值(整型可直接传入,自动转换为double)
* @return cJSON* 成功返回数值节点指针,失败返回NULL
*/
cJSON *cJSON_CreateNumber(double num);
/**
* 向JSON对象添加键值对
* @brief 将子节点作为指定键的值,添加到对象节点中
* @param object 目标对象节点指针
* @param string 键名(字符串)
* @param item 要添加的子节点指针(任意JSON类型节点)
* @return void 无返回值
* @note 子节点的内存由cJSON管理,无需手动释放
*/
void cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
/**
* 向JSON数组添加元素
* @brief 将子节点添加到数组节点的末尾
* @param array 目标数组节点指针
* @param item 要添加的子节点指针(任意JSON类型节点)
* @return void 无返回值
*/
void cJSON_AddItemToArray(cJSON *array, cJSON *item);
完整构造示例
#include <stdio.h>
#include <stdlib.h>
#include "cJSON.h"
int main() {
// 创建顶层对象(根节点)
cJSON *root = cJSON_CreateObject();
if (root == NULL) {
printf("创建根对象失败\n");
return -1;
}
// 向根对象添加基础类型键值对
cJSON_AddItemToObject(root, "name", cJSON_CreateString("zhangsan"));
cJSON_AddItemToObject(root, "age", cJSON_CreateNumber(25));
cJSON_AddItemToObject(root, "is_student", cJSON_CreateBool(0)); // false
// 创建数组并添加元素
cJSON *hobbies = cJSON_CreateArray();
cJSON_AddItemToArray(hobbies, cJSON_CreateString("reading"));
cJSON_AddItemToArray(hobbies, cJSON_CreateString("coding"));
cJSON_AddItemToArray(hobbies, cJSON_CreateString("hiking"));
// 将数组添加到根对象
cJSON_AddItemToObject(root, "hobbies", hobbies);
// 创建嵌套对象并添加
cJSON *address = cJSON_CreateObject();
cJSON_AddItemToObject(address, "province", cJSON_CreateString("Guangdong"));
cJSON_AddItemToObject(address, "city", cJSON_CreateString("Shenzhen"));
cJSON_AddItemToObject(address, "detail", cJSON_CreateString("Nanshan District"));
// 将嵌套对象添加到根对象
cJSON_AddItemToObject(root, "address", address);
// 生成JSON字符串(格式化输出)
char *json_str = cJSON_Print(root);
if (json_str == NULL) {
printf("生成JSON字符串失败\n");
cJSON_Delete(root);
return -1;
}
printf("构造的JSON:\n%s\n", json_str);
// 释放资源
free(json_str);
cJSON_Delete(root);
return 0;
}
输出:
构造的JSON:
{
"name": "zhangsan",
"age": 25,
"is_student": false,
"hobbies": ["reading", "coding", "hiking"],
"address": {
"province": "Guangdong",
"city": "Shenzhen",
"detail": "Nanshan District"
}
}
实战练习:调用聚合天气 API 解析 JSON
核心步骤
- 申请聚合天气 API 接口,获取 API 密钥。
- 用 C 语言通过 HTTP 协议发送请求(如使用 libcurl 库),获取 JSON 格式的响应数据。
- 用 cJSON 库解析响应数据,提取城市、天气、温度、风力等信息。
- 将提取的信息输出到终端。
方式一:HTTP 请求实现(基于 libcurl)
需安装 libcurl 库(sudo apt-get install libcurl4-openssl-dev),编译时链接(gcc main.c cJSON.c -o main -lcurl):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
#include "cJSON.h"
/**
* @brief 用于存储动态增长的响应数据的结构体
*/
typedef struct {
char *data;
size_t size;
} ResponseBuffer;
/**
* @brief libcurl 的回调函数,用于接收 HTTP 响应数据
*
* @param ptr 指向接收到的数据的指针
* @param size 每个数据块的大小(单位:字节)
* @param nmemb 数据块的数量
* @param userdata 用户自定义数据,这里是 ResponseBuffer 结构体指针
* @return size_t 实际处理的数据大小(通常是 size * nmemb)
*/
size_t write_callback(void *ptr, size_t size, size_t nmemb, void *userdata) {
size_t realsize = size * nmemb;
ResponseBuffer *buffer = (ResponseBuffer *)userdata;
// 重新分配内存,为新数据腾出空间
char *temp = (char *)realloc(buffer->data, buffer->size + realsize + 1);
if (temp == NULL) {
// 内存分配失败,这是一个严重错误
fprintf(stderr, "Failed to allocate memory in write_callback\n");
return 0;
}
buffer->data = temp;
// 将新数据复制到缓冲区末尾
memcpy(&(buffer->data[buffer->size]), ptr, realsize);
buffer->size += realsize;
// 确保字符串以 null 字符结尾
buffer->data[buffer->size] = '\0';
return realsize;
}
/**
* @brief 发送 HTTP GET 请求并返回响应体的 JSON 字符串
*
* @param url 请求的 URL
* @return char* 成功则返回动态分配的 JSON 字符串,失败则返回 NULL
*/
char *get_weather_json(const char *url) {
CURL *curl;
CURLcode res;
ResponseBuffer buffer = {0}; // 初始化缓冲区
// 全局初始化 libcurl (建议在程序开始时调用一次)
curl_global_init(CURL_GLOBAL_DEFAULT);
// 初始化一个 curl 会话
curl = curl_easy_init();
if (!curl) {
fprintf(stderr, "curl_easy_init() failed\n");
curl_global_cleanup();
return NULL;
}
// 设置 curl 选项
curl_easy_setopt(curl, CURLOPT_URL, url);
// 设置回调函数和用户数据
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&buffer);
// 执行请求
res = curl_easy_perform(curl);
if (res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
free(buffer.data); // 释放已分配的内存
buffer.data = NULL;
}
// 清理 curl 会话
curl_easy_cleanup(curl);
// 全局清理 libcurl (建议在程序结束时调用一次)
curl_global_cleanup();
// 返回响应数据(调用者需要负责 free)
return buffer.data;
}
int main() {
// !!! 重要:请替换为你的聚合数据 API 密钥 !!!
const char *api_key = "你的API密钥";
const char *city = "Beijing";
char api_url[256];
// 构建完整的 API 请求 URL
snprintf(api_url, sizeof(api_url),
"http://v.juhe.cn/tianqi/query?city=%s&key=%s",
city, api_key);
printf("正在请求天气数据...\nURL: %s\n", api_url);
// 调用函数获取 JSON 字符串
char *json_str = get_weather_json(api_url);
if (json_str == NULL) {
fprintf(stderr, "获取天气数据失败\n");
return 1;
}
printf("\n成功获取到 JSON 数据:\n%s\n", json_str);
// --- 使用 cJSON 解析 JSON 数据 ---
cJSON *root = cJSON_Parse(json_str);
if (root == NULL) {
const char *error_ptr = cJSON_GetErrorPtr();
if (error_ptr != NULL) {
fprintf(stderr, "JSON 解析失败: %s\n", error_ptr);
}
free(json_str); // 释放 JSON 字符串
return 1;
}
// 检查返回状态
cJSON *error_code = cJSON_GetObjectItem(root, "error_code");
if (error_code && error_code->valueint != 0) {
cJSON *reason = cJSON_GetObjectItem(root, "reason");
fprintf(stderr, "API 请求失败: %s (错误码: %d)\n",
reason ? reason->valuestring : "未知错误",
error_code->valueint);
cJSON_Delete(root);
free(json_str);
return 1;
}
// 获取 results 数组
cJSON *results_array = cJSON_GetObjectItem(root, "result");
if (results_array == NULL || !cJSON_IsArray(results_array)) {
printf("未找到 'result' 数组\n");
cJSON_Delete(root);
free(json_str);
return 1;
}
// 使用 cJSON_ArrayForEach 宏遍历数组
cJSON *weather_obj;
cJSON_ArrayForEach(weather_obj, results_array) {
cJSON *city_name = cJSON_GetObjectItem(weather_obj, "city");
cJSON *weather = cJSON_GetObjectItem(weather_obj, "weather");
cJSON *temperature = cJSON_GetObjectItem(weather_obj, "temperature");
cJSON *humidity = cJSON_GetObjectItem(weather_obj, "humidity");
cJSON *wind = cJSON_GetObjectItem(weather_obj, "wind");
cJSON *update_time = cJSON_GetObjectItem(weather_obj, "update_time");
// 打印提取的信息
printf("\n--------------------\n");
printf("城市: %s\n", city_name ? city_name->valuestring : "N/A");
printf("天气: %s\n", weather ? weather->valuestring : "N/A");
printf("温度: %s\n", temperature ? temperature->valuestring : "N/A");
printf("湿度: %s\n", humidity ? humidity->valuestring : "N/A");
printf("风向: %s\n", wind ? wind->valuestring : "N/A");
printf("更新时间: %s\n", update_time ? update_time->valuestring : "N/A");
}
// --- 释放所有资源 ---
cJSON_Delete(root); // 释放 cJSON 对象树
free(json_str); // 释放从 get_weather_json 返回的字符串
return 0;
}
方式二:popen 调用 curl 命令,并通过 fread 读取其输出
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/**
* @brief 使用 popen 和 curl 命令获取 HTTP 响应
*
* @param url 请求的 URL 地址
* @return char* 成功则返回动态分配的响应字符串,失败则返回 NULL
*/
char *get_weather_json_with_popen(const char *url) {
if (url == NULL) {
return NULL;
}
// 构建 curl 命令
// -s: 静默模式,不显示进度条等额外信息
// "%s": 要请求的 URL
char command[1024];
int ret = snprintf(command, sizeof(command), "curl -s \"%s\"", url);
if (ret < 0 || ret >= sizeof(command)) {
fprintf(stderr, "命令字符串太长,可能会被截断。\n");
return NULL;
}
// 使用 popen 执行命令,并以只读方式打开管道
FILE *fp = popen(command, "r");
if (fp == NULL) {
perror("popen 失败");
return NULL;
}
// 读取管道内容
// 使用动态缓冲区来存储读取的数据
char *response = NULL;
size_t buffer_size = 1024; // 初始缓冲区大小
size_t total_bytes_read = 0;
// 初始分配内存
response = (char *)malloc(buffer_size);
if (response == NULL) {
perror("malloc 失败");
pclose(fp);
return NULL;
}
response[0] = '\0'; // 确保初始为空字符串
char chunk[256];
size_t bytes_read;
printf("正在通过 curl 命令获取数据...\n");
// 循环读取,直到文件结束
while ((bytes_read = fread(chunk, 1, sizeof(chunk) - 1, fp)) > 0) {
// 确保 chunk 是一个以 null 结尾的字符串,方便 strcat
chunk[bytes_read] = '\0';
// 检查缓冲区是否足够容纳新读取的数据
if (total_bytes_read + bytes_read >= buffer_size - 1) { // 预留一个字节给 null 结束符
buffer_size *= 2; // 翻倍扩容
char *temp = (char *)realloc(response, buffer_size);
if (temp == NULL) {
perror("realloc 失败");
free(response);
pclose(fp);
return NULL;
}
response = temp;
}
// 将新读取的数据追加到响应字符串末尾
strcat(response, chunk);
total_bytes_read += bytes_read;
}
// 检查 fread 是否因为错误而退出
if (ferror(fp)) {
perror("fread 失败");
free(response);
response = NULL;
}
// 关闭管道
// pclose 会等待子进程 (curl) 结束并回收其资源
if (pclose(fp) == -1) {
perror("pclose 失败");
// 即使 pclose 失败,我们也已经读取了数据,所以可以选择返回它
// 这里为了简化,我们在发生任何错误时都返回 NULL
free(response);
response = NULL;
}
return response;
}
int main() {
// !!! 重要:请替换为你的聚合数据 API 密钥 !!!
const char *api_key = "你的API密钥";
const char *city = "Beijing";
char api_url[256];
// 构建完整的 API 请求 URL
snprintf(api_url, sizeof(api_url),
"http://v.juhe.cn/tianqi/query?city=%s&key=%s",
city, api_key);
// 调用函数获取 JSON 字符串
char *json_str = get_weather_json_with_popen(api_url);
if (json_str == NULL) {
fprintf(stderr, "获取天气数据失败\n");
return 1;
}
printf("成功获取到 JSON 数据:\n%s\n", json_str);
// 在这里可以继续使用 cJSON 解析 json_str ...
// ...
// 释放动态分配的内存
free(json_str);
return 0;
}
拓展:Base64 编码 + 百度 AI 物体识别 API 实战
Base64 编码核心规则
Base64 是一种基于 64 个可打印字符的编码方式,用于将二进制数据转换为文本格式(便于 HTTP 传输):
- 编码表:A-Z(0-25)、a-z(26-51)、0-9(52-61)、+(62)、/(63)。
- 编码原理:3 个字节(24 位)拆分为 4 个 6 位组,每个 6 位组对应编码表中的一个字符。
- 不足 3 字节处理:缺 1 字节补 2 个
=,缺 2 字节补 1 个=。
图片转 Base64 编码(C 语言实现)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Base64编码表
const char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// 将图片文件转换为Base64字符串
char *image_to_base64(const char *img_path) {
// 读取图片二进制数据
FILE *fp = fopen(img_path, "rb");
if (!fp) return NULL;
fseek(fp, 0, SEEK_END);
long img_size = ftell(fp);
fseek(fp, 0, SEEK_SET);
unsigned char *img_data = malloc(img_size);
fread(img_data, 1, img_size, fp);
fclose(fp);
// 计算Base64字符串长度
long base64_len = (img_size + 2) / 3 * 4;
char *base64_str = malloc(base64_len + 1);
memset(base64_str, 0, base64_len + 1);
// 编码核心逻辑
for (long i = 0, j = 0; i < img_size; i += 3, j += 4) {
// 取3个字节
unsigned char byte1 = i < img_size ? img_data[i] : 0;
unsigned char byte2 = i+1 < img_size ? img_data[i+1] : 0;
unsigned char byte3 = i+2 < img_size ? img_data[i+2] : 0;
// 拆分为4个6位组
unsigned int triple = (byte1 << 16) | (byte2 << 8) | byte3;
base64_str[j] = base64_table[(triple >> 18) & 0x3F];
base64_str[j+1] = base64_table[(triple >> 12) & 0x3F];
base64_str[j+2] = i+1 < img_size ? base64_table[(triple >> 6) & 0x3F] : '=';
base64_str[j+3] = i+2 < img_size ? base64_table[triple & 0x3F] : '=';
}
free(img_data);
return base64_str;
}
调用百度 AI 物体识别 API
- 百度 AI 开放平台申请 “物体识别” API,获取 API Key 和 Secret Key,生成访问令牌(Access Token)。
- 将图片 Base64 编码字符串构造为 JSON 请求体:
{
"image": "图片的Base64编码(去掉前缀data:image/jpg;base64,)",
"image_type": "BASE64"
}
- 用 HTTP POST 请求发送 JSON 数据,接收响应后用 cJSON 解析识别结果(如物体名称、置信度等)。

浙公网安备 33010602011771号