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

cnblogs 2024-06-15 08:09:01 阅读 88

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

用于请求心知天气的信息, 现在的信息, 未来n天的气象信息. 使用域名通过TCP连接到心知天气服务器, 采用cJSON进行解析.模块化实现, 可选择英文、中文;天数;城市;

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

目录

  • 序言
    • 功能描述
    • 运行结果示范
    • 注意!
  • 代码
    • weather_api.h
    • weather_api.c
    • demo.c
    • cJSON.h
    • cJSON.c
  • 参考链接

序言

功能描述

用于请求心知天气的信息, 现在的信息, 未来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文件.

参考链接

  • 心知服务的说明
  • 新知天气城市列表
  • DaveGamble/cJSON: Ultralightweight JSON parser in ANSI C (github.com) cJSON库 基于C语言的一个开源项目

测试链接:

未来天气

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



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。