基于cJSON及心知天气模块化实现获取城市气象信息(现在、未来)

V1.0 2024年6月14日 发布于博客园

序言

功能描述

用于请求心知天气的信息, 现在的信息, 未来n天的气象信息(免费版仅能3天).

使用域名通过TCP连接到心知天气服务器, 采用cJSON进行解析.

模块化实现, 可选择英文、中文;天数;城市;

运行结果示范

天气服务器域名 api.seniverse.com 的IP地址是 116.62.81.138

位置 ID: WS0E9D8WN298
位置名称: 广州
国家: CN
位置: 广州,广州,广东,中国
时区: Asia/Shanghai
时区偏移: +08:00
天气情况: 阴
天气代码: 9
温度: 26
最后更新时间: 2024-06-14T20:52:05+08:00


城市 ID:         WS0E9D8WN298
城市名称:        广州
国家:            CN
具体位置:        广州,广州,广东,中国
时区:            Asia/Shanghai
时区偏移:        +08:00

日期:                    2024-06-14
白天天气现象文字:        小雨
白天天气现象代码:        13
夜间天气现象文字:        暴雨
夜间天气现象代码:        16
当天最高气温:    31
当天最低气温:    26
降水量:         19.53
降水概率:        0.98
风向文字:        西南
风向角度:        225
风速:            8.4
风力等级:        2
相对湿度:        91

日期:                    2024-06-15
白天天气现象文字:        暴雨
白天天气现象代码:        16
夜间天气现象文字:        中雨
夜间天气现象代码:        14
当天最高气温:    31
当天最低气温:    26
降水量:         28.09
降水概率:        0.98
风向文字:        南
风向角度:        180
风速:            23.4
风力等级:        4
相对湿度:        87

日期:                    2024-06-16
白天天气现象文字:        中雨
白天天气现象代码:        14
夜间天气现象文字:        雷阵雨
夜间天气现象代码:        11
当天最高气温:    32
当天最低气温:    26
降水量:         18.96
降水概率:        0.97
风向文字:        南
风向角度:        180
风速:            15.3
风力等级:        3
相对湿度:        87
数据更新时间:    2024-06-14T08:00:00+08:00


信阳气象信息

位置 ID: WT9NG9P4ZDHG
位置名称: 信阳
国家: CN
位置: 信阳,信阳,河南,中国
时区: Asia/Shanghai
时区偏移: +08:00
天气情况: 晴
天气代码: 1
温度: 32
最后更新时间: 2024-06-14T20:48:23+08:00


城市 ID:         WT9NG9P4ZDHG
城市名称:        信阳
国家:            CN
具体位置:        信阳,信阳,河南,中国
时区:            Asia/Shanghai
时区偏移:        +08:00

日期:                    2024-06-14
白天天气现象文字:        阴
白天天气现象代码:        9
夜间天气现象文字:        晴
夜间天气现象代码:        1
当天最高气温:    39
当天最低气温:    25
降水量:         0.00
降水概率:        0.00
风向文字:        东
风向角度:        90
风速:            23.4
风力等级:        4
相对湿度:        65

日期:                    2024-06-15
白天天气现象文字:        晴
白天天气现象代码:        0
夜间天气现象文字:        阴
夜间天气现象代码:        9
当天最高气温:    39
当天最低气温:    27
降水量:         0.00
降水概率:        0.00
风向文字:        北
风向角度:        0
风速:            8.4
风力等级:        2
相对湿度:        53

日期:                    2024-06-16
白天天气现象文字:        小雨
白天天气现象代码:        13
夜间天气现象文字:        阴
夜间天气现象代码:        9
当天最高气温:    35
当天最低气温:    25
降水量:         19.45
降水概率:        0.99
风向文字:        东北
风向角度:        45
风速:            3.0
风力等级:        1
相对湿度:        95
数据更新时间:    2024-06-14T08:00:00+08:00

注意!

weather_api.h中需要填写自己的私钥

#define KEY "XXXXXXXXX" // 自己的私钥

代码

weather_api.h

#ifndef _WEATHER_API_H
#define _WEATHER_API_H
/**
 * @file name : weather_api.h
 * @brief     : 用于请求心知天气的信息, 现在的信息, 未来n天的气象信息(免费版仅能3天).
 *              使用域名通过TCP连接到心知天气服务器, 采用cJSON进行解析.
 *              模块化实现, 可选择英文、中文;天数;城市;
 * @author    : RISE_AND_GRIND@163.com
 * @date      : 2024年6月14日
 * @version   : 1.0
 * @note      :
 * CopyRight (c)  2023-2024   RISE_AND_GRIND@163.com   All Right Reseverd
 */
/***************************************头文件***************************************/
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "cJSON.h"

/***************************************END******************************************/
/***************************************预处理***************************************/
// 目标服务器ip地址 端口号
// #define IPADDR "116.62.81.138"
#define SERVERHOSTNAME "api.seniverse.com" // 服务器域名
#define PORT 80
#define MAX_STRING_LENGTH 100 // 解析到的JSON键值的最大长度
#define BUFFERSIZE 4096       // 缓冲区大小
#define DAYS_COUNT 3          // 要查询的未来几天天气
// 配置信息 访问频限 20次/分钟 国内370个主要城市
#define KEY "XXXXXXXXX" // 自己的私钥
#define LOCAL "guangzhou"       // 解析的城市
#define TYPEINFO 0              // 1默认输出解析到的信息, 0关闭
/**
 * 国内 370 个主要城市
 * 1 天气实况,包括天气现象文字、代码和气温 3 项数据
 * 2 未来 3 天天气预报,包括白天天气现象文字及代码、晚间天气现象文字及代码、当天最高温度和最低温度、风向风速
 * 3 6 项基本类生活指数,包括穿衣、紫外线强度、洗车、旅游、感冒、运动指数
 */
/***************************************END******************************************/
/***************************************数据类型***************************************/
// 请求的位置信息
typedef struct
{
    char id[MAX_STRING_LENGTH];              // 城市ID
    char name[MAX_STRING_LENGTH];            // 城市名称
    char country[MAX_STRING_LENGTH];         // 国家
    char path[MAX_STRING_LENGTH];            // 具体位置
    char timezone[MAX_STRING_LENGTH];        // 时区
    char timezone_offset[MAX_STRING_LENGTH]; // 时区偏移
} Location;
// 返回指定days天数的结果 定义未来天气结构体 未来15天/24小时  每天更新2次,8点和20点
typedef struct
{
    char date[MAX_STRING_LENGTH];                  // 日期
    char text_day[MAX_STRING_LENGTH];              // 白天天气现象文字:阴晴多云等
    char code_day[MAX_STRING_LENGTH];              // 白天天气现象代码
    char text_night[MAX_STRING_LENGTH];            // 夜间天气现象文字:阴晴多云等
    char code_night[MAX_STRING_LENGTH];            // 夜间天气现象代码
    char high[MAX_STRING_LENGTH];                  // 当天最高气温
    char low[MAX_STRING_LENGTH];                   // 当天最低气温
    char rainfall[MAX_STRING_LENGTH];              // 降水量:当天累计降水量,毫米(mm)
    char precip[MAX_STRING_LENGTH];                // 降水概率,范围0~100,单位百分比(目前仅支持国外城市)
    char wind_direction[MAX_STRING_LENGTH];        // 风向文字:地面10米风向,东、东南、南、西南等
    char wind_direction_degree[MAX_STRING_LENGTH]; // 风向角度,范围0~360, 地面10米风向,东、东南、南、西南等
    char wind_speed[MAX_STRING_LENGTH];            // 风速,单位km/h(当unit=c时)、mph(当unit=f时)
    char wind_scale[MAX_STRING_LENGTH];            // 风力等级:根据风速大小划分的等级
    char humidity[MAX_STRING_LENGTH];              // 相对湿度:地面2米湿度,百分比%
} Daily;

// 天气实况信息
typedef struct
{
    char text[MAX_STRING_LENGTH];        // 天气现象文字
    char code[MAX_STRING_LENGTH];        // 天气现象代码
    char temperature[MAX_STRING_LENGTH]; // 温度,单位为c摄氏度或f华氏度
} Now;

// 天气实况
typedef struct
{
    Location location;                   // 请求的位置信息
    Now now;                             //
    char last_update[MAX_STRING_LENGTH]; // 数据更新时间(该城市的本地时间)
} WeatherResults;
// 未来 3 天天气预报
typedef struct
{
    Location location;                   // 请求的位置信息
    Daily daily[DAYS_COUNT];             // 返回指定days天数的结果
    char last_update[MAX_STRING_LENGTH]; // 数据更新时间(该城市的本地时间)
} WeatherResultsFuture;
/***************************************END******************************************/
/**************************************函数声明**************************************/
/** 解析天气实况JSON信息
 * @name      parse_weather
 * @brief     解析天气实况JSON信息
 * @param     json_data JSON格式的字符串(未解析的格式, 从接收区收到)
 * @param     weather_results 接收天气实况解析结果的结构体
 * @date      2024年6月14日
 * @version   1.0
 * @note      使用前提:WeatherResultsFuture weather_results_future;
 */
void parse_weather(const char *json_data, WeatherResults *weather_results);

/** 解析未来3天天气JSON信息
 * @name      parse_weather_future
 * @brief     解析未来3天天气JSON信息
 * @param     json_data JSON格式的字符串(未解析的格式, 从接收区收到)
 * @param     weather_results_future 接收未来天气解析结果的结构体
 * @date      2024年6月14日
 * @version   1.0
 * @note      使用前提:WeatherResultsFuture weather_results_future;
 */
void parse_weather_future(const char *json_data, WeatherResultsFuture *weather_results_future);
/***************************************END******************************************/
// 打印解析后的天气实况信息
void PrintWeatherInfo(WeatherResults const *weather_results);
// 打印解析后的未来3天天气信息
void PrintWeatherFutureInfo(WeatherResultsFuture const *weather_results_future);
/**
 * @brief 解析域名得到 IPv4 地址或 IPv6 地址,优先 IPv4
 * @param hostname 域名字符串
 * @param ipaddr 用于接收结果的字符数组
 * @return 解析到 IPv4 地址返回 4,解析到 IPv6 地址返回 6,失败返回 -1
 */
int resolve_hostname(const char *hostname, char *ipaddr);
/** 连接天气服务器
 * @name      connectServer
 * @brief     连接天气服务器
 * @param     ipaddr 服务器ip地址
 * @param     port 服务器端口
 * @param     tcp_socket_fd 网络套接字描述符
 * @return    成功 0; 失败-1 (会自动关闭套接字)
 * @date      2024年6月14日
 * @version   1.0
 * @note      前提: int tcp_socket_fd=-1;//创建公用网络套接字描述符
 *            注意: 会创建tcp套接字, 不会自动关闭! 需要做错误处理
 *            失败会自动关闭套接字文件, 且tcp_socket_fd=-1;
 *            成功, 用完记得手动关闭且tcp_socket_fd;
 *
 */
int connectServer(const char *ipaddr, int port, int *tcp_socket_fd);
/** 请求天气实况信息
 * @name      getWeatherInfo
 * @brief     请求天气实况信息并解析
 * @param     tcp_socket_fd 网络套接字描述符
 * @param     local 城市位置拼音 例如 "guangzhou"
 * @param     language 接收的语言 例如 "zh-Hans"表示中文  英文:"en"
 * @param     weather_results 天气实况接收结解析结果构体
 * @date      2024年6月14日
 * @version   1.0
 * @note      中文:zh-Hans 英文:en
 *  使用前提: connectServer(); char recvbuf[BUFFERSIZE]; // 接收缓冲区    WeatherResults weather_results;
 */
void getWeatherInfo(int tcp_socket_fd, char const *local, char const *language, char *recvbuf, WeatherResults *weather_results);

/** 请求未来天气JSON信息
 * @name      getWeatherFutureInfo
 * @brief     请求未来天气JSON信息
 * @param     tcp_socket_fd 网络套接字描述符
 * @param     local 城市位置拼音 例如 "guangzhou"
 * @param     language 接收的语言 例如 "zh-Hans"表示中文  英文:"en"
 * @param     weather_results 天气实况接收结解析结果构体
 * @date      2024年6月14日
 * @version   1.0
 * @note      中文:zh-Hans 英文:en
 *  使用前提: connectServer(); char recvbuf[BUFFERSIZE]; // 接收缓冲区    WeatherResultsFuture weather_results_future;
 */
void getWeatherFutureInfo(int tcp_socket_fd, char const *local, char const *language, char *recvbuf, WeatherResultsFuture *weather_results_future);
#endif //_WEATHER_API_H

weather_api.c

/**
 * @file name : weather_api.c
 * @brief     : 用于请求心知天气的信息, 现在的信息, 未来n天的气象信息(免费版仅能3天).
 *              使用域名通过TCP连接到心知天气服务器, 采用cJSON进行解析.
 *              模块化实现, 可选择英文、中文;天数;城市;
 * @author    : RISE_AND_GRIND@163.com
 * @date      : 2024年6月14日
 * @version   : 1.0
 * @note      :
 * CopyRight (c)  2023-2024   RISE_AND_GRIND@163.com   All Right Reseverd
 */
#include "weather_api.h"

/** 解析天气实况JSON信息
 * @name      parse_weather
 * @brief     解析天气实况JSON信息
 * @param     json_data JSON格式的字符串(未解析的格式, 从接收区收到)
 * @param     weather_results 接收天气实况解析结果的结构体
 * @date      2024年6月14日
 * @version   1.0
 * @note      使用前提:WeatherResultsFuture weather_results_future;
 */
void parse_weather(const char *json_data, WeatherResults *weather_results)
{
    //  使用cJSON库解析JSON字符串
    cJSON *json = cJSON_Parse(json_data);
    if (json == NULL)
    {
        fprintf(stderr, "解析天气实况 JSON 错误\n");
        return;
    }
#if TYPEINFO

    // 把解析之后的JSON格式结构体进行输出
    printf("实时天气是\n%s\n", cJSON_Print(json));
#endif
    // 获取JSON对象中的"results"数组
    cJSON *results = cJSON_GetObjectItem(json, "results");
    if (results == NULL || !cJSON_IsArray(results))
    {
        fprintf(stderr, "查找“results”数组时出错\n");
        cJSON_Delete(json);
        return;
    }

    // 获取"results"数组中的第一个元素
    cJSON *result = cJSON_GetArrayItem(results, 0); // 假设数组中只有一个结果
    if (result == NULL)
    {
        fprintf(stderr, "在“results”数组中查找第一项时出错\n");
        cJSON_Delete(json);
        return;
    }

    // 获取"result"对象中的"location"对象
    cJSON *location = cJSON_GetObjectItem(result, "location");
    if (location != NULL)
    {
        // 将"location"对象中的各个字段值复制到weather_results->location结构体中
        strncpy(weather_results->location.id, cJSON_GetObjectItem(location, "id")->valuestring, MAX_STRING_LENGTH);
        strncpy(weather_results->location.name, cJSON_GetObjectItem(location, "name")->valuestring, MAX_STRING_LENGTH);
        strncpy(weather_results->location.country, cJSON_GetObjectItem(location, "country")->valuestring, MAX_STRING_LENGTH);
        strncpy(weather_results->location.path, cJSON_GetObjectItem(location, "path")->valuestring, MAX_STRING_LENGTH);
        strncpy(weather_results->location.timezone, cJSON_GetObjectItem(location, "timezone")->valuestring, MAX_STRING_LENGTH);
        strncpy(weather_results->location.timezone_offset, cJSON_GetObjectItem(location, "timezone_offset")->valuestring, MAX_STRING_LENGTH);
    }

    // 获取"result"对象中的"now"对象
    cJSON *now = cJSON_GetObjectItem(result, "now");
    if (now != NULL)
    {
        // 将"now"对象中的各个字段值复制到weather_results->now结构体中
        strncpy(weather_results->now.text, cJSON_GetObjectItem(now, "text")->valuestring, MAX_STRING_LENGTH);
        strncpy(weather_results->now.code, cJSON_GetObjectItem(now, "code")->valuestring, MAX_STRING_LENGTH);
        strncpy(weather_results->now.temperature, cJSON_GetObjectItem(now, "temperature")->valuestring, MAX_STRING_LENGTH);
    }

    // 将"result"对象中的"last_update"字段值复制到weather_results->last_update字符数组中
    strncpy(weather_results->last_update, cJSON_GetObjectItem(result, "last_update")->valuestring, MAX_STRING_LENGTH);

    // 释放cJSON对象
    cJSON_Delete(json);
}

/** 解析未来n天天气JSON信息
 * @name      parse_weather_future
 * @brief     解析未来3天天气JSON信息
 * @param     json_data JSON格式的字符串(未解析的格式, 从接收区收到)
 * @param     weather_results_future 接收未来天气解析结果的结构体
 * @date      2024年6月14日
 * @version   1.0
 * @note      使用前提:WeatherResultsFuture weather_results_future;
 */
void parse_weather_future(const char *json_data, WeatherResultsFuture *weather_results_future)
{
    // 使用cJSON库解析JSON字符串
    cJSON *json = cJSON_Parse(json_data);
    if (json == NULL)
    {
        fprintf(stderr, "Error parsing JSON\n");
        return;
    }
#if TYPEINFO
    // 把解析之后的JSON格式结构体进行输出
    printf("未来天气是\n%s\n", cJSON_Print(json));
#endif
    // 获取JSON对象中的"results"数组
    cJSON *results = cJSON_GetObjectItem(json, "results");
    if (results == NULL || !cJSON_IsArray(results))
    {
        fprintf(stderr, "查找“results”数组时出错\n");
        cJSON_Delete(json);
        return;
    }

    // 获取"results"数组中的第一个元素
    cJSON *result = cJSON_GetArrayItem(results, 0); // 假设数组中只有一个结果
    if (result == NULL)
    {
        fprintf(stderr, "在“results”数组中查找第一项时出错\n");
        cJSON_Delete(json);
        return;
    }

    // 获取"result"对象中的"location"对象
    cJSON *location = cJSON_GetObjectItem(result, "location");
    if (location != NULL)
    {
        // 将"location"对象中的各个字段值复制到weather_results_future->location结构体中
        strncpy(weather_results_future->location.id, cJSON_GetObjectItem(location, "id")->valuestring, MAX_STRING_LENGTH);
        strncpy(weather_results_future->location.name, cJSON_GetObjectItem(location, "name")->valuestring, MAX_STRING_LENGTH);
        strncpy(weather_results_future->location.country, cJSON_GetObjectItem(location, "country")->valuestring, MAX_STRING_LENGTH);
        strncpy(weather_results_future->location.path, cJSON_GetObjectItem(location, "path")->valuestring, MAX_STRING_LENGTH);
        strncpy(weather_results_future->location.timezone, cJSON_GetObjectItem(location, "timezone")->valuestring, MAX_STRING_LENGTH);
        strncpy(weather_results_future->location.timezone_offset, cJSON_GetObjectItem(location, "timezone_offset")->valuestring, MAX_STRING_LENGTH);
    }

    // 获取"result"对象中的"daily"数组
    cJSON *daily = cJSON_GetObjectItem(result, "daily");
    if (daily != NULL && cJSON_IsArray(daily))
    {
        // 遍历"daily"数组中的每一天的天气数据
        for (int i = 0; i < DAYS_COUNT; i++)
        {
            cJSON *day = cJSON_GetArrayItem(daily, i);
            if (day != NULL)
            {
                // 将"day"对象中的各个字段值复制到weather_results_future->daily[i]结构体中
                strncpy(weather_results_future->daily[i].date, cJSON_GetObjectItem(day, "date")->valuestring, MAX_STRING_LENGTH);
                strncpy(weather_results_future->daily[i].text_day, cJSON_GetObjectItem(day, "text_day")->valuestring, MAX_STRING_LENGTH);
                strncpy(weather_results_future->daily[i].code_day, cJSON_GetObjectItem(day, "code_day")->valuestring, MAX_STRING_LENGTH);
                strncpy(weather_results_future->daily[i].text_night, cJSON_GetObjectItem(day, "text_night")->valuestring, MAX_STRING_LENGTH);
                strncpy(weather_results_future->daily[i].code_night, cJSON_GetObjectItem(day, "code_night")->valuestring, MAX_STRING_LENGTH);
                strncpy(weather_results_future->daily[i].high, cJSON_GetObjectItem(day, "high")->valuestring, MAX_STRING_LENGTH);
                strncpy(weather_results_future->daily[i].low, cJSON_GetObjectItem(day, "low")->valuestring, MAX_STRING_LENGTH);
                strncpy(weather_results_future->daily[i].rainfall, cJSON_GetObjectItem(day, "rainfall")->valuestring, MAX_STRING_LENGTH);
                strncpy(weather_results_future->daily[i].precip, cJSON_GetObjectItem(day, "precip")->valuestring, MAX_STRING_LENGTH);
                strncpy(weather_results_future->daily[i].wind_direction, cJSON_GetObjectItem(day, "wind_direction")->valuestring, MAX_STRING_LENGTH);

                strncpy(weather_results_future->daily[i].wind_direction_degree, cJSON_GetObjectItem(day, "wind_direction_degree")->valuestring, MAX_STRING_LENGTH);

                strncpy(weather_results_future->daily[i].wind_speed, cJSON_GetObjectItem(day, "wind_speed")->valuestring, MAX_STRING_LENGTH);
                strncpy(weather_results_future->daily[i].wind_scale, cJSON_GetObjectItem(day, "wind_scale")->valuestring, MAX_STRING_LENGTH);
                strncpy(weather_results_future->daily[i].humidity, cJSON_GetObjectItem(day, "humidity")->valuestring, MAX_STRING_LENGTH);
            }
        }
    }

    // 将"result"对象中的"last_update"字段值复制到weather_results_future->last_update字符数组中
    strncpy(weather_results_future->last_update, cJSON_GetObjectItem(result, "last_update")->valuestring, MAX_STRING_LENGTH);

    // 释放cJSON对象
    cJSON_Delete(json);
}

// 打印解析后的天气实况信息
void PrintWeatherInfo(WeatherResults const *weather_results)
{
    // 打印解析后的结构体内容
    printf("\n");
    printf("位置 ID: %s\n", weather_results->location.id);
    printf("位置名称: %s\n", weather_results->location.name);
    printf("国家: %s\n", weather_results->location.country);
    printf("位置: %s\n", weather_results->location.path);
    printf("时区: %s\n", weather_results->location.timezone);
    printf("时区偏移: %s\n", weather_results->location.timezone_offset);

    printf("天气情况: %s\n", weather_results->now.text);
    printf("天气代码: %s\n", weather_results->now.code);
    printf("温度: %s\n", weather_results->now.temperature);

    printf("最后更新时间: %s\n", weather_results->last_update);
    printf("\n");
}

// 打印解析后的未来3天天气信息
void PrintWeatherFutureInfo(WeatherResultsFuture const *weather_results_future)
{

    // 打印解析后的结构体内容
    printf("\n");
    printf("城市 ID:\t %s\n", weather_results_future->location.id);
    printf("城市名称:\t %s\n", weather_results_future->location.name);
    printf("国家:\t\t %s\n", weather_results_future->location.country);
    printf("具体位置:\t %s\n", weather_results_future->location.path);
    printf("时区:\t\t %s\n", weather_results_future->location.timezone);
    printf("时区偏移:\t %s\n", weather_results_future->location.timezone_offset);

    for (int i = 0; i < DAYS_COUNT; i++)
    {
        printf("\n");

        printf("日期:\t\t\t %s\n", weather_results_future->daily[i].date);
        printf("白天天气现象文字:\t %s\n", weather_results_future->daily[i].text_day);
        printf("白天天气现象代码:\t %s\n", weather_results_future->daily[i].code_day);
        printf("夜间天气现象文字:\t %s\n", weather_results_future->daily[i].text_night);
        printf("夜间天气现象代码:\t %s\n", weather_results_future->daily[i].code_night);
        printf("当天最高气温:\t %s\n", weather_results_future->daily[i].high);
        printf("当天最低气温:\t %s\n", weather_results_future->daily[i].low);
        printf("降水量:\t %s\n", weather_results_future->daily[i].rainfall);
        printf("降水概率:\t %s\n", weather_results_future->daily[i].precip);
        printf("风向文字:\t %s\n", weather_results_future->daily[i].wind_direction);
        printf("风向角度:\t %s\n", weather_results_future->daily[i].wind_direction_degree);
        printf("风速:\t\t %s\n", weather_results_future->daily[i].wind_speed);
        printf("风力等级:\t %s\n", weather_results_future->daily[i].wind_scale);
        printf("相对湿度:\t %s\n", weather_results_future->daily[i].humidity);
    }

    printf("数据更新时间:\t %s\n", weather_results_future->last_update);
    printf("\n");
}

/** 解析域名得到 IPv4 地址或 IPv6 地址
 * @brief 解析域名得到 IPv4 地址或 IPv6 地址,优先 IPv4
 * @param hostname 域名字符串
 * @param ipaddr 用于接收结果的字符数组
 * @return 解析到 IPv4 地址返回 4,解析到 IPv6 地址返回 6,失败返回 -1
 */
int resolve_hostname(const char *hostname, char *ipaddr)
{
    // 定义 addrinfo 结构体变量,用于存储地址信息
    struct addrinfo hints, *res, *p;
    int errcode; // 存储 getaddrinfo 函数的返回值
    void *ptr;   // 指向 IP 地址的指针

    // 清空 hints 结构体并设置相关参数
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;     // 不指定 IPv4 或 IPv6
    hints.ai_socktype = SOCK_STREAM; // TCP 流套接字

    // 调用 getaddrinfo 函数获取主机名对应的地址信息
    errcode = getaddrinfo(hostname, NULL, &hints, &res);
    if (errcode != 0)
    {
        // 如果 getaddrinfo 返回非零值,则表示出错,打印错误信息并返回 -1
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(errcode));
        return -1;
    }

    // 遍历返回的地址信息链表
    for (p = res; p != NULL; p = p->ai_next)
    {
        // 判断地址族是否为 IPv4
        if (p->ai_family == AF_INET)
        {
            // 将 IPv4 地址赋给 ptr
            ptr = &((struct sockaddr_in *)p->ai_addr)->sin_addr;
            // 将地址转换为字符串形式,并存储在 ipaddr 中
            inet_ntop(p->ai_family, ptr, ipaddr, INET6_ADDRSTRLEN);
            freeaddrinfo(res); // 释放 getaddrinfo 分配的内存
            return 4;          // 返回 4 表示解析到 IPv4 地址
        }
    }

    // 如果没有找到 IPv4 地址,继续寻找 IPv6 地址
    for (p = res; p != NULL; p = p->ai_next)
    {
        // 判断地址族是否为 IPv6
        if (p->ai_family == AF_INET6)
        {
            // 将 IPv6 地址赋给 ptr
            ptr = &((struct sockaddr_in6 *)p->ai_addr)->sin6_addr;
            // 将地址转换为字符串形式,并存储在 ipaddr 中
            inet_ntop(p->ai_family, ptr, ipaddr, INET6_ADDRSTRLEN);
            freeaddrinfo(res); // 释放 getaddrinfo 分配的内存
            return 6;          // 返回 6 表示解析到 IPv6 地址
        }
    }

    // 如果没有找到任何地址,释放 getaddrinfo 分配的内存并返回 -1
    freeaddrinfo(res);
    return -1;
}

/** 连接天气服务器
 * @name      connectServer
 * @brief     连接天气服务器
 * @param     ipaddr 服务器ip地址
 * @param     port 服务器端口
 * @param     tcp_socket_fd 网络套接字描述符
 * @return    成功 0; 失败-1 (会自动关闭套接字)
 * @date      2024年6月14日
 * @version   1.0
 * @note      前提: int tcp_socket_fd=-1;//创建公用网络套接字描述符
 *            注意: 会创建tcp套接字, 不会自动关闭! 需要做错误处理
 *            失败会自动关闭套接字文件, 且tcp_socket_fd=-1;
 *            成功, 用完记得手动关闭且tcp_socket_fd;
 *
 */
int connectServer(const char *ipaddr, int port, int *tcp_socket_fd)
{
    /**********************1.创建TCP套接字**********************/
    *tcp_socket_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (*tcp_socket_fd == -1)
    {
        fprintf(stderr, "创建TCP套接字错误, errno:%d,%s\n", errno, strerror(errno));
        exit(1);
    }

    /******************2.发起连接请求,等待接受服务器接受连接 建立TCP连接*******************/
    struct sockaddr_in dest_addr;
    dest_addr.sin_family = AF_INET;                // 协议族,是固定的
    dest_addr.sin_port = htons(PORT);              // 服务器端口,必须转换为网络字节序
                                                   // dest_addr.sin_addr.s_addr = inet_addr(IPADDR); // 服务器地址
    dest_addr.sin_addr.s_addr = inet_addr(ipaddr); // 服务器地址

    /******************3. 客户端连接成功*******************************/
    int ret = connect(*tcp_socket_fd, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
    if (ret < 0)
    {
        fprintf(stderr, "客户端连接失败, errno:%d,%s\n", errno, strerror(errno));
        close(*tcp_socket_fd);
        printf("TCP套接字已经关闭!\n");
        *tcp_socket_fd = -1; // 恢复失败的情况
        return -1;
    }
    return 0;
}

/** 请求天气实况信息
 * @name      getWeatherInfo
 * @brief     请求天气实况信息并解析
 * @param     tcp_socket_fd 网络套接字描述符
 * @param     local 城市位置拼音 例如 "guangzhou"
 * @param     language 接收的语言 例如 "zh-Hans"表示中文  英文:"en"
 * @param     weather_results 天气实况接收结解析结果构体
 * @date      2024年6月14日
 * @version   1.0
 * @note      中文:zh-Hans 英文:en
 *  使用前提: connectServer(); char recvbuf[BUFFERSIZE]; // 接收缓冲区    WeatherResults weather_results;
 */
void getWeatherInfo(int tcp_socket_fd, char const *local, char const *language, char *recvbuf, WeatherResults *weather_results)
{
    /**************************4. 构建实时天气HTTP请求内容****************************************/
    // 用于存储HTTP的请求内容: 请求行 + 请求字段 + \r\n + 请求包体(可选)
    char reqbuf[1024] = {0}; // 储存http请求内容临时缓存区
    sprintf(reqbuf, "GET https://api.seniverse.com/v3/weather/now.json?key=%s&location=%s&language=%s&unit=c "
                    "HTTP/1.1"
                    "\r\n"
                    "Host:api.seniverse.com\r\n"
                    "\r\n",
            KEY, local, language);
    /**************************5. 发送http请求内容****************************************/
    // 双方建立连接,此时可以利用HTTP协议发送请求信息,并等待服务器的响应  基于请求/响应
    send(tcp_socket_fd, reqbuf, strlen(reqbuf), 0);
    /**************************6. 等待服务器的响应****************************************/
    /*************请求实时天气**************** */
    bzero(recvbuf, BUFFERSIZE); // 清除缓冲区
    // 第一次返回的响应参数
    recv(tcp_socket_fd, recvbuf, BUFFERSIZE, 0); // 从TCP套接字接收数据

#if TYPEINFO
    printf("%s\n", recvbuf); // 打印接收到的响应参数
#endif
    bzero(recvbuf, BUFFERSIZE);
    // 第二次返回的响应包体
    recv(tcp_socket_fd, recvbuf, BUFFERSIZE, 0);
#if TYPEINFO
    printf("%s\n", recvbuf); // 打印接收到的响应包体
#endif
    /*************解析实时天气**************** */
    parse_weather(recvbuf, weather_results); // 调用解析函数,解析接收到的JSON数据并填充结构体

    // 打印解析后的结构体内容
    PrintWeatherInfo(weather_results);
}

/** 请求未来天气JSON信息
 * @name      getWeatherFutureInfo
 * @brief     请求未来天气JSON信息
 * @param     tcp_socket_fd 网络套接字描述符
 * @param     local 城市位置拼音 例如 "guangzhou"
 * @param     language 接收的语言 例如 "zh-Hans"表示中文  英文:"en"
 * @param     weather_results 天气实况接收结解析结果构体
 * @date      2024年6月14日
 * @version   1.0
 * @note      中文:zh-Hans 英文:en
 *  使用前提: connectServer(); char recvbuf[BUFFERSIZE]; // 接收缓冲区    WeatherResultsFuture weather_results_future;
 */
void getWeatherFutureInfo(int tcp_socket_fd, char const *local, char const *language, char *recvbuf, WeatherResultsFuture *weather_results_future)
{
    /*********************************请求未来2天天气************************************** */
    /**************************4. 构建未来天气HTTP请求内容****************************************/
    // 用于存储HTTP的请求内容: 请求行 + 请求字段 + \r\n + 请求包体(可选)
    char reqbuf[1024] = {0}; // 储存http请求内容临时缓存区
    sprintf(reqbuf, "GET https://api.seniverse.com/v3/weather/daily.json?key=%s&location=%s&language=%s&unit=c&start=0&days=%d "
                    "HTTP/1.1"
                    "\r\n"
                    "Host:api.seniverse.com\r\n"
                    "\r\n",
            KEY, local, language, DAYS_COUNT);
    /**************************5. 发送http请求内容****************************************/
    // 双方建立连接,此时可以利用HTTP协议发送请求信息,并等待服务器的响应  基于请求/响应
    send(tcp_socket_fd, reqbuf, strlen(reqbuf), 0);

    /**************************6. 等待服务器的响应****************************************/
    /*************请求实时天气**************** */
    bzero(recvbuf, BUFFERSIZE); // 清空接收缓冲区
    // 第一次返回的响应参数
    recv(tcp_socket_fd, recvbuf, BUFFERSIZE, 0); // 从TCP套接字接收数据
#if TYPEINFO
    printf("%s\n", recvbuf); // 打印接收到的响应参数
#endif                       // 打印接收到的数据
    bzero(recvbuf, BUFFERSIZE);
    // 第二次返回的响应包体
    recv(tcp_socket_fd, recvbuf, BUFFERSIZE, 0);
#if TYPEINFO
    printf("%s\n", recvbuf); // 打印接收到的响应包体
#endif
    /*************解析未来天气**************** */

    parse_weather_future(recvbuf, weather_results_future); // 调用解析函数,解析接收到的JSON数据并填充结构体
    // 打印解析后的结构体内容
    PrintWeatherFutureInfo(weather_results_future);
}

// 使用示范
#if 0
#include <stdio.h>
#include "cJSON.h"
#include "weather_api.h"

int main(int argc, char const *argv[])
{
    /***********域名解析****************/
    char ipaddr[64] = {0};
    if (resolve_hostname(SERVERHOSTNAME, ipaddr) != -1)
    {
        printf("天气服务器域名 %s 的IP地址是 %s\n", SERVERHOSTNAME, ipaddr);
    }
    else
    {
        printf(" %s 解析域名失败\n", SERVERHOSTNAME);
    }
    // TCP连接服务器
    int tcp_socket_fd = -1;
    if (connectServer(ipaddr, PORT, &tcp_socket_fd) != 0)
    {
        perror("连接天气服务器失败");
        return -1;
    }

    /**************************请求实时天气HTTP请求****************************************/
    char recvbuf[BUFFERSIZE]; // 接收缓冲区
    WeatherResults weather_results;
    getWeatherInfo(tcp_socket_fd, LOCAL, "zh-Hans", recvbuf, &weather_results);

    WeatherResultsFuture weather_results_future;
    getWeatherFutureInfo(tcp_socket_fd, LOCAL, "zh-Hans", recvbuf, &weather_results_future);
    printf("\n信阳气象信息\n");
    getWeatherInfo(tcp_socket_fd, "xinyang", "zh-Hans", recvbuf, &weather_results);
    getWeatherFutureInfo(tcp_socket_fd, "xinyang", "zh-Hans", recvbuf, &weather_results_future);
    close(tcp_socket_fd);
    return 0;
}

#endif

demo.c

下面是使用示范

#include <stdio.h>
#include "weather_api.h"

int main(int argc, char const *argv[])
{
    /***********域名解析****************/
    char ipaddr[64] = {0};
    if (resolve_hostname(SERVERHOSTNAME, ipaddr) != -1)
    {
        printf("天气服务器域名 %s 的IP地址是 %s\n", SERVERHOSTNAME, ipaddr);
    }
    else
    {
        printf(" %s 解析域名失败\n", SERVERHOSTNAME);
    }
    // TCP连接服务器
    int tcp_socket_fd = -1;
    if (connectServer(ipaddr, PORT, &tcp_socket_fd) != 0)
    {
        perror("连接天气服务器失败");
        return -1;
    }

    /**************************请求实时天气HTTP请求****************************************/
    char recvbuf[BUFFERSIZE]; // 接收缓冲区
    WeatherResults weather_results;
    getWeatherInfo(tcp_socket_fd, LOCAL, "zh-Hans", recvbuf, &weather_results);

    WeatherResultsFuture weather_results_future;
    getWeatherFutureInfo(tcp_socket_fd, LOCAL, "zh-Hans", recvbuf, &weather_results_future);
    printf("\n信阳气象信息\n");
    getWeatherInfo(tcp_socket_fd, "xinyang", "zh-Hans", recvbuf, &weather_results);
    getWeatherFutureInfo(tcp_socket_fd, "xinyang", "zh-Hans", recvbuf, &weather_results_future);
    close(tcp_socket_fd);
    return 0;
}

cJSON.h

下面是我汉化过的cJSON.h

// clang-format off

/*
  Copyright (c) 2009-2017 Dave Gamble and cJSON contributors

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.
*/
/*
  版权所有 (c) 2009-2017 Dave Gamble 和 cJSON 贡献者

  特此免费授予任何获得本软件及相关文档文件(“软件”)副本的个人,不受限制地处理本软件,包括但不限于使用、复制、修改、合并、发布、分发、再许可和/或销售软件副本,并允许向其提供本软件的人员这样做,须符合以下条件:

  上述版权声明和本许可声明应包含在本软件的所有副本或主要部分中。

  本软件按“原样”提供,不提供任何形式的明示或暗示担保,包括但不限于适销性、适用性和非侵权的担保。在任何情况下,作者或版权持有人均不对因本软件或本软件的使用或其他交易引起的任何索赔、损害或其他责任承担责任,无论是在合同诉讼中、侵权行为中还是其他方面。
*/

#ifndef cJSON__h
#define cJSON__h

#ifdef __cplusplus
extern "C"
{
#endif

#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
#define __WINDOWS__
#endif

#ifdef __WINDOWS__

  /* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention.  For windows you have 3 define options:

  CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
  CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
  CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol

  For *nix builds that support visibility attribute, you can define similar behavior by

  setting default visibility to hidden by adding
  -fvisibility=hidden (for gcc)
  or
  -xldscope=hidden (for sun cc)
  to CFLAGS

  then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does

  */
  /* 在为 Windows 编译时,我们指定一个特定的调用约定,以避免在从具有不同默认调用约定的项目中调用时出现问题。对于 Windows,有 3 个定义选项:

  CJSON_HIDE_SYMBOLS - 在不希望导出符号时定义
  CJSON_EXPORT_SYMBOLS - 在库构建时定义以导出符号(默认)
  CJSON_IMPORT_SYMBOLS - 在希望导入符号时定义

  对于支持可见性属性的 *nix 构建,您可以通过以下方式定义类似的行为:

  通过添加 -fvisibility=hidden(对于 gcc)或 -xldscope=hidden(对于 sun cc)到 CFLAGS 来设置默认可见性为隐藏

  然后使用 CJSON_API_VISIBILITY 标志以与 CJSON_EXPORT_SYMBOLS 相同的方式“导出”相同的符号
  */
#define CJSON_CDECL __cdecl
#define CJSON_STDCALL __stdcall

/* export symbols by default, this is necessary for copy pasting the C and header file */
/* 默认导出符号,这是复制粘贴 C 和头文件所必需的 */
#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
#define CJSON_EXPORT_SYMBOLS
#endif

#if defined(CJSON_HIDE_SYMBOLS)
#define CJSON_PUBLIC(type) type CJSON_STDCALL
#elif defined(CJSON_EXPORT_SYMBOLS)
#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL
#elif defined(CJSON_IMPORT_SYMBOLS)
#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL
#endif
#else /* !__WINDOWS__ */
#define CJSON_CDECL
#define CJSON_STDCALL

#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined(__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type
#else
#define CJSON_PUBLIC(type) type
#endif
#endif

/* project version */
/* 项目版本 */
#define CJSON_VERSION_MAJOR 1
#define CJSON_VERSION_MINOR 7
#define CJSON_VERSION_PATCH 18

#include <stddef.h>

/* cJSON Types: */
/* cJSON 类型: */
#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 */ /* 原始 JSON */

#define cJSON_IsReference 256
#define cJSON_StringIsConst 512

  /* The cJSON structure: */
  /* cJSON 结构体定义: */
  typedef struct cJSON
  {
    /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
    /* next/prev 允许您遍历数组/对象链。或者,使用 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 */
    /* 项目的字符串,如果 type==cJSON_String 和 type == cJSON_Raw */
    char *valuestring;
    /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
    /* 写入 valueint 已弃用,请改用 cJSON_SetNumberValue */
    int valueint;
    /* The item's number, if type==cJSON_Number */
    /* 项目的数字,如果 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 钩子函数结构体 */
  typedef struct cJSON_Hooks
  {
    /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */
    /* malloc/free 在 Windows 上是 CDECL,无论编译器的默认调用约定如何,因此确保钩子允许直接传递这些函数。 */
    void *(CJSON_CDECL *malloc_fn)(size_t sz);
    void(CJSON_CDECL *free_fn)(void *ptr);
  } cJSON_Hooks;

  typedef int cJSON_bool;

  /* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
   * This is to prevent stack overflows. */
  /* 限制数组/对象嵌套的深度,超过此深度 cJSON 将拒绝解析。防止堆栈溢出。 */
#ifndef CJSON_NESTING_LIMIT
#define CJSON_NESTING_LIMIT 1000
#endif

  /* returns the version of cJSON as a string */
  /**
   * @name      cJSON_Version
   * @brief     返回 cJSON 版本号的字符串
   * @return    const char* 版本号字符串
   */
  CJSON_PUBLIC(const char *)  cJSON_Version(void);

  /* Supply malloc, realloc and free functions to cJSON */
  /**
   * @name      cJSON_InitHooks
   * @brief     提供 malloc、realloc 和 free 函数给 cJSON 使用
   * @param     hooks cJSON_Hooks 结构体指针,包含自定义的 malloc 和 free 函数
   */
  CJSON_PUBLIC(void)  cJSON_InitHooks(cJSON_Hooks *hooks);

  /* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
  /* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
  /**
   * @name      cJSON_Parse
   * @brief     解析 JSON 字符串并返回 cJSON 对象
   * @param     value JSON 字符串
   * @return    cJSON* 解析后的 cJSON 对象
   * @note      调用者负责释放返回的 cJSON 对象(使用 cJSON_Delete)
   */
  CJSON_PUBLIC(cJSON *)  cJSON_Parse(const char *value);
  /**
   * @name      cJSON_ParseWithLength
   * @brief     解析指定长度的 JSON 字符串并返回 cJSON 对象
   * @param     value JSON 字符串
   * @param     buffer_length 字符串长度
   * @return    cJSON* 解析后的 cJSON 对象
   * @note      调用者负责释放返回的 cJSON 对象(使用 cJSON_Delete)
   */
  CJSON_PUBLIC(cJSON *)  cJSON_ParseWithLength(const char *value, size_t buffer_length);
  /* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
  /* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
  /**
   * @name      cJSON_ParseWithOpts
   * @brief     解析 JSON 字符串并返回 cJSON 对象,允许指定是否要求 JSON 以 null 结尾,并检索解析结束的指针
   * @param     value JSON 字符串
   * @param     return_parse_end 解析结束的指针
   * @param     require_null_terminated 是否要求 JSON 以 null 结尾
   * @return    cJSON* 解析后的 cJSON 对象
   * @note      调用者负责释放返回的 cJSON 对象(使用 cJSON_Delete)
   */
  CJSON_PUBLIC(cJSON *)  cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
  /**
   * @name      cJSON_ParseWithLengthOpts
   * @brief     解析指定长度的 JSON 字符串并返回 cJSON 对象,允许指定是否要求 JSON 以 null 结尾,并检索解析结束的指针
   * @param     value JSON 字符串
   * @param     buffer_length 字符串长度
   * @param     return_parse_end 解析结束的指针
   * @param     require_null_terminated 是否要求 JSON 以 null 结尾
   * @return    cJSON* 解析后的 cJSON 对象
   * @note      调用者负责释放返回的 cJSON 对象(使用 cJSON_Delete)
   */
  CJSON_PUBLIC(cJSON *)  cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated);

  /* Render a cJSON entity to text for transfer/storage. */

  /**
   * @name      cJSON_Print
   * @brief     将 cJSON 对象渲染为文本(带格式)
   * @param     item cJSON 对象
   * @return    char* 渲染后的文本
   * @note      调用者负责释放返回的字符串(使用 stdlib free、cJSON_Hooks.free_fn 或 cJSON_free)
   */
  CJSON_PUBLIC(char *)  cJSON_Print(const cJSON *item);

  /* Render a cJSON entity to text for transfer/storage without any formatting. */
  /**
   * @name      cJSON_PrintUnformatted
   * @brief     将 cJSON 对象渲染为文本(无格式)
   * @param     item cJSON 对象
   * @return    char* 渲染后的文本
   * @note      调用者负责释放返回的字符串(使用 stdlib free、cJSON_Hooks.free_fn 或 cJSON_free)
   */
  CJSON_PUBLIC(char *)  cJSON_PrintUnformatted(const cJSON *item);

  /* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
  /**
   * @name      cJSON_PrintBuffered
   * @brief     使用缓冲策略将 cJSON 对象渲染为文本
   * @param     item cJSON 对象
   * @param     prebuffer 预缓冲大小
   * @param     fmt 是否格式化输出
   * @return    char* 渲染后的文本
   * @note      调用者负责释放返回的字符串(使用 stdlib free、cJSON_Hooks.free_fn 或 cJSON_free)
   */
  CJSON_PUBLIC(char *)  cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
  /* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
  /* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
  /**
   * @name      cJSON_PrintPreallocated
   * @brief     使用已分配的缓冲区将 cJSON 对象渲染为文本
   * @param     item cJSON 对象
   * @param     buffer 已分配的缓冲区
   * @param     length 缓冲区长度
   * @param     format 是否格式化输出
   * @return    cJSON_bool 成功返回 1,失败返回 0
   * @note      调用者对缓冲区完全负责
   */
  CJSON_PUBLIC(cJSON_bool)  cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
  /* Delete a cJSON entity and all subentities. */
  /**
   * @name      cJSON_Delete
   * @brief     删除 cJSON 对象及其所有子对象
   * @param     item cJSON 对象
   */
  CJSON_PUBLIC(void)  cJSON_Delete(cJSON *item);

  /* Returns the number of items in an array (or object). */
  /**
   * @name      cJSON_GetArraySize
   * @brief     返回数组(或对象)中的项数
   * @param     array cJSON 数组
   * @return    int 数组中的项数
   */
  CJSON_PUBLIC(int)  cJSON_GetArraySize(const cJSON *array);

  /* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */
  /**
   * @name      cJSON_GetArrayItem
   * @brief     从数组中检索指定索引的项
   * @param     array cJSON 数组
   * @param     index 索引
   * @return    cJSON* 检索到的项,失败返回 NULL
   */
  CJSON_PUBLIC(cJSON *)  cJSON_GetArrayItem(const cJSON *array, int index);
  /* Get item "string" from object. Case insensitive. */

  /**
   * @name      cJSON_GetObjectItem
   * @brief     从对象中获取指定名称的项(不区分大小写)
   * @param     object cJSON 对象
   * @param     string 项的名称
   * @return    cJSON* 检索到的项
   */
  CJSON_PUBLIC(cJSON *)  cJSON_GetObjectItem(const cJSON *const object, const char *const string);
  /**
   * @name      cJSON_GetObjectItemCaseSensitive
   * @brief     从对象中获取指定名称的项(区分大小写)
   * @param     object cJSON 对象
   * @param     string 项的名称
   * @return    cJSON* 检索到的项
   */
  CJSON_PUBLIC(cJSON *)  cJSON_GetObjectItemCaseSensitive(const cJSON *const object, const char *const string);

  /**
   * @name      cJSON_HasObjectItem
   * @brief     检查对象中是否存在指定名称的项
   * @param     object cJSON 对象
   * @param     string 项的名称
   * @return    cJSON_bool 存在返回 1,不存在返回 0
   */
  CJSON_PUBLIC(cJSON_bool)  cJSON_HasObjectItem(const cJSON *object, const char *string);
  /* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */

  /**
   * @name      cJSON_GetErrorPtr
   * @brief     获取解析失败的错误指针
   * @return    const char* 错误指针
   */
  CJSON_PUBLIC(const char *)  cJSON_GetErrorPtr(void);

  /* Check item type and return its value */
  /**
   * @name      cJSON_GetStringValue
   * @brief     检查项类型并返回其字符串值
   * @param     item cJSON 项
   * @return    char* 字符串值
   */
  CJSON_PUBLIC(char *)  cJSON_GetStringValue(const cJSON *const item);
  /**
   * @name      cJSON_GetNumberValue
   * @brief     检查项类型并返回其数值
   * @param     item cJSON 项
   * @return    double 数值
   */
  CJSON_PUBLIC(double)  cJSON_GetNumberValue(const cJSON *const item);

  /* These functions check the type of an item */
  /* 以下函数检查项的类型 */
  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);

  /* These calls create a cJSON item of the appropriate type. */
  /* 以下函数创建适当类型的 cJSON 项 */
  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 */
  /* 原始 JSON */
  CJSON_PUBLIC(cJSON *)  cJSON_CreateRaw(const char *raw);
  CJSON_PUBLIC(cJSON *)  cJSON_CreateArray(void);
  CJSON_PUBLIC(cJSON *)  cJSON_CreateObject(void);

  /* Create a string where valuestring references a string so
   * it will not be freed by cJSON_Delete */
  /**
   * @name      cJSON_CreateStringReference
   * @brief     创建一个字符串引用项,valuestring 引用一个字符串,因此不会被 cJSON_Delete 释放
   * @param     string 字符串
   * @return    cJSON* 创建的字符串引用项
   */
  CJSON_PUBLIC(cJSON *)  cJSON_CreateStringReference(const char *string);
  /* Create an object/array that only references it's elements so
   * they will not be freed by cJSON_Delete */
  /**
   * @name      cJSON_CreateObjectReference
   * @brief     创建一个对象引用项,引用其元素,因此不会被 cJSON_Delete 释放
   * @param     child 子对象
   * @return    cJSON* 创建的对象引用项
   */
  CJSON_PUBLIC(cJSON *)  cJSON_CreateObjectReference(const cJSON *child);
  /**
   * @name      cJSON_CreateArrayReference
   * @brief     创建一个数组引用项,引用其元素,因此不会被 cJSON_Delete 释放
   * @param     child 子数组
   * @return    cJSON* 创建的数组引用项
   */
  CJSON_PUBLIC(cJSON *)  cJSON_CreateArrayReference(const cJSON *child);

  /* These utilities create an Array of count items.
   * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/
  /**
   * @name      cJSON_CreateIntArray
   * @brief     创建一个包含整数数组的 cJSON 项
   * @param     numbers 整数数组
   * @param     count 数组元素数量
   * @return    cJSON* 创建的整数数组项
   * @note      参数 count 不能大于整数数组的元素数量,否则数组访问将越界
   */
  CJSON_PUBLIC(cJSON *)  cJSON_CreateIntArray(const int *numbers, int count);
  /**
   * @name      cJSON_CreateFloatArray
   * @brief     创建一个包含浮点数组的 cJSON 项
   * @param     numbers 浮点数组
   * @param     count 数组元素数量
   * @return    cJSON* 创建的浮点数组项
   * @note      参数 count 不能大于浮点数组的元素数量,否则数组访问将越界
   */
  CJSON_PUBLIC(cJSON *)  cJSON_CreateFloatArray(const float *numbers, int count);
  /**
   * @name      cJSON_CreateDoubleArray
   * @brief     创建一个包含双精度数组的 cJSON 项
   * @param     numbers 双精度数组
   * @param     count 数组元素数量
   * @return    cJSON* 创建的双精度数组项
   * @note      参数 count 不能大于双精度数组的元素数量,否则数组访问将越界
   */
  CJSON_PUBLIC(cJSON *)  cJSON_CreateDoubleArray(const double *numbers, int count);
  /**
   * @name      cJSON_CreateStringArray
   * @brief     创建一个包含字符串数组的 cJSON 项
   * @param     strings 字符串数组
   * @param     count 数组元素数量
   * @return    cJSON* 创建的字符串数组项
   * @note
   *
   */
  CJSON_PUBLIC(cJSON *)  cJSON_CreateStringArray(const char *const *strings, int count);

  /* Append item to the specified array/object. */
  /**
   * @name      cJSON_AddItemToArray
   * @brief     将项添加到指定的数组中
   * @param     array cJSON 数组
   * @param     item 要添加的项
   * @return    cJSON_bool 成功返回 1,失败返回 0
   */
  CJSON_PUBLIC(cJSON_bool)  cJSON_AddItemToArray(cJSON *array, cJSON *item);
  /*@name cJSON_AddItemToObject
   *@brief 将项添加到指定的对象中
   *@param object cJSON 对象
   *@param string 项的名称
   *@param item 要添加的项
   *@ return cJSON_bool 成功返回 1,失败返回 0 *
   */
  CJSON_PUBLIC(cJSON_bool)  cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
  /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
   * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
   * writing to `item->string` */
  /**
   * @name      cJSON_AddItemToObjectCS
   * @brief     将项添加到指定的对象中,当字符串是 const 时使用
   * @param     object cJSON 对象
   * @param     string 项的名称
   * @param     item 要添加的项
   * @return    cJSON_bool 成功返回 1,失败返回 0
   * @note      当使用此函数时,确保在写入 `item->string` 之前始终检查 (item->type & cJSON_StringIsConst) 是否为零
   */
  CJSON_PUBLIC(cJSON_bool)  cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
  /* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
  /**
   * @name      cJSON_AddItemReferenceToArray
   * @brief     将项的引用添加到指定的数组中
   * @param     array cJSON 数组
   * @param     item 要添加的项
   * @return    cJSON_bool 成功返回 1,失败返回 0
   * @note      使用此函数时,您希望将现有的 cJSON 添加到新的 cJSON 中,但不希望破坏现有的 cJSON
   */
  CJSON_PUBLIC(cJSON_bool)  cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
  /**
   * @name      cJSON_AddItemReferenceToObject
   * @brief     将项的引用添加到指定的对象中
   * @param     object cJSON 对象
   * @param     string 项的名称
   * @param     item 要添加的项
   * @return    cJSON_bool 成功返回 1,失败返回 0
   * @note      使用此函数时,您希望将现有的 cJSON 添加到新的 cJSON 中,但不希望破坏现有的 cJSON
   */
  CJSON_PUBLIC(cJSON_bool)  cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);

  /* Remove/Detach items from Arrays/Objects. */
  /**
   * @name      cJSON_DetachItemViaPointer
   * @brief     从父对象中分离指定的项
   * @param     parent 父对象
   * @param     item 要分离的项
   * @return    cJSON* 分离的项
   */
  CJSON_PUBLIC(cJSON *)  cJSON_DetachItemViaPointer(cJSON *parent, cJSON *const item);

  /**
   * @name      cJSON_DetachItemFromArray
   * @brief     从数组中分离指定索引的项
   * @param     array cJSON 数组
   * @param     which 索引
   * @return    cJSON* 分离的项
   */
  CJSON_PUBLIC(cJSON *)  cJSON_DetachItemFromArray(cJSON *array, int which);

  /**
   * @name      cJSON_DeleteItemFromArray
   * @brief     从数组中删除指定索引的项
   * @param     array cJSON 数组
   * @param     which 索引
   */
  CJSON_PUBLIC(void)  cJSON_DeleteItemFromArray(cJSON *array, int which);
  /**
   * @name      cJSON_DetachItemFromObject
   * @brief     从对象中分离指定名称的项(不区分大小写)
   * @param     object cJSON 对象
   * @param     string 项的名称
   * @return    cJSON* 分离的项
   */
  CJSON_PUBLIC(cJSON *)  cJSON_DetachItemFromObject(cJSON *object, const char *string);
  /**
   * @name      cJSON_DetachItemFromObjectCaseSensitive
   * @brief     从对象中分离指定名称的项(区分大小写)
   * @param     object cJSON 对象
   * @param     string 项的名称
   * @return    cJSON* 分离的项
   */
  CJSON_PUBLIC(cJSON *)  cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
  /**
   * @name      cJSON_DeleteItemFromObject
   * @brief     从对象中删除指定名称的项(不区分大小写)
   * @param     object cJSON 对象
   * @param     string 项的名称
   */
  CJSON_PUBLIC(void)  cJSON_DeleteItemFromObject(cJSON *object, const char *string);
  /**
   * @name      cJSON_DeleteItemFromObjectCaseSensitive
   * @brief     从对象中删除指定名称的项(区分大小写)
   * @param     object cJSON 对象
   * @param     string 项的名称
   */
  CJSON_PUBLIC(void)  cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);

  /* Update array items. */
  /**
   * @name      cJSON_InsertItemInArray
   * @brief     在数组中插入项,将现有项右移
   * @param     array cJSON 数组
   * @param     which 插入位置的索引
   * @param     newitem 新项
   * @return    cJSON_bool 成功返回 1,失败返回 0
   */
  CJSON_PUBLIC(cJSON_bool)  cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
  /**
   * @name      cJSON_ReplaceItemViaPointer
   * @brief     用新项替换父对象中的指定项
   * @param     parent 父对象
   * @param     item 要替换的项
   * @param     replacement 新项
   * @return    cJSON_bool 成功返回 1,失败返回 0
   */
  CJSON_PUBLIC(cJSON_bool)  cJSON_ReplaceItemViaPointer(cJSON *const parent, cJSON *const item, cJSON *replacement);
  /**
   * @name      cJSON_ReplaceItemInArray
   * @brief     用新项替换数组中指定索引的项
   * @param     array cJSON 数组
   * @param     which 索引
   * @param     newitem 新项
   * @return    cJSON_bool 成功返回 1,失败返回 0
   */
  CJSON_PUBLIC(cJSON_bool)  cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
  /**
   * @name      cJSON_ReplaceItemInObject
   * @brief     用新项替换对象中指定名称的项(不区分大小写)
   * @param     object cJSON 对象
   * @param     string 项的名称
   * @param     newitem 新项
   * @return    cJSON_bool 成功返回 1,失败返回 0
   */
  CJSON_PUBLIC(cJSON_bool)  cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem);
  /**
   * @name      cJSON_ReplaceItemInObjectCaseSensitive
   * @brief     用新项替换对象中指定名称的项(区分大小写)
   * @param     object cJSON 对象
   * @param     string 项的名称
   * @param     newitem 新项
   * @return    cJSON_bool 成功返回 1,失败返回 0
   */
  CJSON_PUBLIC(cJSON_bool)  cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem);

  /* Duplicate a cJSON item */
  /**
   * @name      cJSON_Duplicate
   * @brief     复制一个 cJSON 项
   * @param     item 要复制的项
   * @param     recurse 是否递归复制子项
   * @return    cJSON* 复制后的项
   * @note      复制将创建一个新的、与传递的项相同的 cJSON 项,在新内存中,需要释放。
   *            当 recurse!=0 时,它将复制连接到该项的任何子项。返回的项的 item->next 和 ->prev 指针始终为零。
   */
  CJSON_PUBLIC(cJSON *)  cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
  /* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
   * need to be released. With recurse!=0, it will duplicate any children connected to the item.
   * The item->next and ->prev pointers are always zero on return from Duplicate. */
  /* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
   * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
  /**
   * @name      cJSON_Compare
   * @brief     递归比较两个 cJSON 项是否相等。如果 a 或 b 为 NULL 或无效,它们将被视为不相等。
   * @param     a 第一个 cJSON 项
   * @param     b 第二个 cJSON 项
   * @param     case_sensitive 是否区分大小写
   * @return    cJSON_bool 相等返回 1,不相等返回 0
   */
  CJSON_PUBLIC(cJSON_bool)  cJSON_Compare(const cJSON *const a, const cJSON *const b, const cJSON_bool case_sensitive);

  /* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings.
   * The input pointer json cannot point to a read-only address area, such as a string constant,
   * but should point to a readable and writable address area. */
  /**
   * @name      cJSON_Minify
   * @brief     压缩字符串,去除字符串中的空白字符(如 ' ', '\t', '\r', '\n')
   * @param     json 要压缩的字符串
   * @note      输入指针 json 不能指向只读地址区域,如字符串常量,而应指向可读写的地址区域。
   */
  CJSON_PUBLIC(void)  cJSON_Minify(char *json);

  /* Helper functions for creating and adding items to an object at the same time.
   * They return the added item or NULL on failure. */

  /**
   * @name      cJSON_AddNullToObject
   * @brief     创建并添加一个空项到对象中
   * @param     object cJSON 对象
   * @param     name 项的名称
   * @return    cJSON* 添加的项,失败返回 NULL
   */
  CJSON_PUBLIC(cJSON *)  cJSON_AddNullToObject(cJSON *const object, const char *const name);
  /**
   * @name      cJSON_AddTrueToObject
   * @brief     创建并添加一个 true 项到对象中
   * @param     object cJSON 对象
   * @param     name 项的名称
   * @return    cJSON* 添加的项,失败返回 NULL
   */
  CJSON_PUBLIC(cJSON *)  cJSON_AddTrueToObject(cJSON *const object, const char *const name);
  /**
   * @name      cJSON_AddFalseToObject
   * @brief     创建并添加一个 false 项到对象中
   * @param     object cJSON 对象
   * @param     name 项的名称
   * @return    cJSON* 添加的项,失败返回 NULL
   */
  CJSON_PUBLIC(cJSON *)  cJSON_AddFalseToObject(cJSON *const object, const char *const name);
  /**
   * @name      cJSON_AddBoolToObject
   * @brief     创建并添加一个布尔项到对象中
   * @param     object cJSON 对象
   * @param     name 项的名称
   * @param     boolean 布尔值
   * @return    cJSON* 添加的项,失败返回 NULL
   */
  CJSON_PUBLIC(cJSON *)  cJSON_AddBoolToObject(cJSON *const object, const char *const name, const cJSON_bool boolean);
  /**
   * @name      cJSON_AddNumberToObject
   * @brief     创建并添加一个数字项到对象中
   * @param     object cJSON 对象
   * @param     name 项的名称
   * @param     number 数值
   * @return    cJSON* 添加的项,失败返回 NULL
   */
  CJSON_PUBLIC(cJSON *)  cJSON_AddNumberToObject(cJSON *const object, const char *const name, const double number);
  /**
   * @name      cJSON_AddStringToObject
   * @brief     创建并添加一个字符串项到对象中
   * @param     object cJSON 对象
   * @param     name 项的名称
   * @param     string 字符串值
   * @return    cJSON* 添加的项,失败返回 NULL
   */
  CJSON_PUBLIC(cJSON *)  cJSON_AddStringToObject(cJSON *const object, const char *const name, const char *const string);
  /**
   * @name      cJSON_AddRawToObject
   * @brief     创建并添加一个原始 JSON 项到对象中
   * @param     object cJSON 对象
   * @param     name 项的名称
   * @param     raw 原始 JSON 字符串
   * @return    cJSON* 添加的项,失败返回 NULL
   */
  CJSON_PUBLIC(cJSON *)  cJSON_AddRawToObject(cJSON *const object, const char *const name, const char *const raw);

  /**
   * @name      cJSON_AddObjectToObject
   * @brief     创建并添加一个对象项到对象中
   * @param     object cJSON 对象
   * @param     name 项的名称
   * @return    cJSON* 添加的项,失败返回 NULL
   */
  CJSON_PUBLIC(cJSON *)  cJSON_AddObjectToObject(cJSON *const object, const char *const name);
  /**
   * @name      cJSON_AddArrayToObject
   * @brief     创建并添加一个数组项到对象中
   * @param     object cJSON 对象
   * @param     name 项的名称
   * @return    cJSON* 添加的项,失败返回 NULL
   */
  CJSON_PUBLIC(cJSON *)  cJSON_AddArrayToObject(cJSON *const object, const char *const name);

/* When assigning an integer value, it needs to be propagated to valuedouble too. */
#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
  /* helper for the cJSON_SetNumberValue macro */
  /**
   * @name      cJSON_SetNumberHelper
   * @brief     帮助宏 cJSON_SetNumberValue 设置数值
   * @param     object cJSON 对象
   * @param     number 数值
   * @return    double 设置后的数值
   */
  CJSON_PUBLIC(double)  cJSON_SetNumberHelper(cJSON *object, double number);
#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
  /* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */
  /**
   * @name      cJSON_SetValuestring
   * @brief     更改 cJSON_String 对象的 valuestring 值,仅当对象类型为 cJSON_String 时生效
   * @param     object cJSON 对象
   * @param     valuestring 新的字符串值
   * @return    char* 更改后的字符串
   */
  CJSON_PUBLIC(char *)  cJSON_SetValuestring(cJSON *object, const char *valuestring);

/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/
#define cJSON_SetBoolValue(object, boolValue) ( \
    (object != NULL && ((object)->type & (cJSON_False | cJSON_True))) ? (object)->type = ((object)->type & (~(cJSON_False | cJSON_True))) | ((boolValue) ? cJSON_True : cJSON_False) : cJSON_Invalid)

/* Macro for iterating over an array or object */
/* */
/*  */
/**
 * 用于在数组或对象上迭代的宏 , 用于遍历一个cJSON数组中的每一个元素
 * 
 * element 是当前遍历到的cJSON元素.
 * array 是需要遍历的cJSON数组。
 * for 循环的初始化部分:element = (array != NULL) ? (array)->child : NULL,如果 array 非空,则将 element 初始化为 array 的第一个子元素,否则初始化为 NULL。
 * 循环的条件部分:element != NULL,只要 element 不为空,循环就会继续。
 * 循环的迭代部分:element = element->next,将 element 更新为下一个兄弟元素。
 */
#define cJSON_ArrayForEach(element, array) for (element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)

  /* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
  /**
   * @name      cJSON_malloc
   * @brief     使用已经设置的 malloc 函数分配内存
   * @param     size 内存大小
   * @return    void* 分配的内存指针
   */
  CJSON_PUBLIC(void *)  cJSON_malloc(size_t size);
  /**
   * @name      cJSON_free
   * @brief     使用已经设置的 free 函数释放内存
   * @param     object 要释放的内存指针
   */
  CJSON_PUBLIC(void)  cJSON_free(void *object);

#ifdef __cplusplus
}
#endif

#endif
// clang-format on

cJSON.c

DaveGamble/cJSON: Ultralightweight JSON parser in ANSI C (github.com) cJSON库 基于C语言的一个开源项目

去这里下载cJSON.c文件及原版cJSON.h文件.

参考链接

测试链接:

未来天气
https://api.seniverse.com/v3/weather/daily.json?key=你的私钥&location=guangzhou&language=en&unit=c&start=0&days=3
实况天气
https://api.seniverse.com/v3/weather/now.json?key=你的私钥&location=guangzhou&language=en&unit=c

热门相关:浴火重生:恶魔五小姐   超萌迷糊妻:BOSS大人别这样   重生之悠然人生   无敌天下   霸道女皇爱上我