Exercise:JSON解析
练习:利用某些平台(聚合 API、百度 A、科大讯飞 API)的 API接口,利用 HTTP 协议向服务器发送请求,并接受服务器的响应,要求利用cISON库对服务器的响应数据进行解析,并输出到终端。
/*****************************************************************************************************
* Function: parse_json_response
* Description: 解析从HTTP响应中接收到的JSON字符串,并打印出格式化的JSON以及提取的关键信息。
* Calls: cJSON_Parse, cJSON_Print, cJSON_GetObjectItem, cJSON_GetArrayItem, cJSON_Delete
* Called By: main
* Input: const char *response - 指向包含HTTP头和JSON响应体的完整响应字符串的指针。
* Output: 打印到标准输出的格式化JSON字符串和提取的关键信息。
* Return: void - 此函数不返回任何值。
* Others:
* 1.待解析的JSON
* {
* "results": [{
* "id": "WX4FBXXFKE4F",
* "name": "北京",
* "country": "CN",
* "path": "北京,北京,中国",
* "timezone": "Asia/Shanghai",
* "timezone_offset": "+08:00"
* }]
* }
* 2.此函数假设response参数是一个有效的HTTP响应,包含一个JSON响应体。
* 如果response格式不正确或解析失败,将打印错误信息到标准错误。
* 3.注意HTTP请求后会有HTTP响应头和JSON响应体,在JSON解析时要分离出来
* 4.请求信息过于大时,会申请的堆空间可能会无法满足,要考虑空间大小
* 5.当HTTP回复的消息不完整时,会出现段错误,要让recv()函数循环接收完全。
* 6.将宏定义中的KEY,替换成自己的
* 7.本文采用“心知天气”的相关API
*
* CopyRight (c) 2023-2024 User_laubon@163.com All Right Reserved
*********************************************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include "cJSON.h"
#define PORT 80
#define HOSTNAME "api.seniverse.com"
#define KEY "your_key"
#define LOCAL "guangzhou"
#define BUFFSIZE 102400
/************************************************** 创建TCP套接字 **********************************************************/
int create_tcp_socket() {
int tcp_socket = socket(AF_INET, SOCK_STREAM, 0);
if (tcp_socket == -1) {
fprintf(stderr, "tcp socket error, errno:%d, %s\n", errno, strerror(errno));
exit(1);
}
return tcp_socket;
}
/************************************************** 解析主机名 **********************************************************/
struct sockaddr_in resolve_hostname(const char *hostname) {
struct hostent *he = gethostbyname(hostname);
if (he == NULL) {
fprintf(stderr, "gethostbyname error\n");
exit(1);
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr = *((struct in_addr *)he->h_addr);
return addr;
}
/************************************************** 连接到服务器 **********************************************************/
void connect_to_server(int socket, struct sockaddr_in addr) {
if (connect(socket, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
fprintf(stderr, "connect error, errno:%d, %s\n", errno, strerror(errno));
exit(1);
}
}
/************************************************** 发送HTTP请求 **********************************************************/
void send_http_request(int socket, const char *hostname, const char *key) {
char reqbuf[1024];
sprintf(reqbuf,
"GET https://api.seniverse.com/v3/location/search.json?key=%s&q=WX4FBXXFKE4F HTTP/1.1\r\n"
"Host:%s\r\n"
"\r\n",
key, hostname);
if (send(socket, reqbuf, strlen(reqbuf), 0) < 0) {
fprintf(stderr, "send error, errno:%d, %s\n", errno, strerror(errno));
exit(1);
}
}
/************************************************** 接收HTTP响应 **********************************************************/
char *receive_http_response(int socket) {
char *response = malloc(BUFFSIZE); // 分配内存以接收响应
if (response == NULL) {
fprintf(stderr, "malloc failed\n");
exit(1);
}
int bytes_received;
int total_bytes_received = 0;
do {
bytes_received = recv(socket, response + total_bytes_received, BUFFSIZE - total_bytes_received - 1, 0);
if (bytes_received > 0) {
total_bytes_received += bytes_received;
}
} while (bytes_received > 0);
if (bytes_received < 0) {
fprintf(stderr, "recv error, errno:%d, %s\n", errno, strerror(errno));
free(response);
exit(1);
}
response[total_bytes_received] = '\0'; // 确保响应字符串以空字符结尾
return response;
}
/************************************************** 解析JSON响应 **********************************************************/
void parse_json_response(const char *response) {
// 查找HTTP响应头和响应体之间的空行,将HTTP响应头和JSON响应体分开
const char *json_data = strstr(response, "\r\n\r\n");
if (json_data != NULL) {
json_data += 4; // 跳过空行,指向JSON数据的开始
} else {
fprintf(stderr, "Failed to find the end of the HTTP headers.\n");
return;
}
cJSON *json = cJSON_Parse(json_data);
if (json == NULL) {
fprintf(stderr, "Error before: [%s]\n", cJSON_GetErrorPtr());
} else {
char *json_str = cJSON_Print(json);
printf("%s\n", json_str); // 打印格式化的JSON字符串
cJSON * result = cJSON_GetObjectItem(json,"results");
cJSON * array = cJSON_GetArrayItem(result,0);
cJSON * id = cJSON_GetObjectItem(array,"id");
cJSON * name = cJSON_GetObjectItem(array,"name");
cJSON * country = cJSON_GetObjectItem(array,"country");
cJSON * path = cJSON_GetObjectItem(array,"path");
cJSON * timezone = cJSON_GetObjectItem(array,"timezone");
cJSON * timezone_offset = cJSON_GetObjectItem(array,"timezone_offset");
printf("id : %s\n", id->valuestring);
printf("name : %s\n", name->valuestring);
printf("country : %s\n", country->valuestring);
printf("path : %s\n", path->valuestring);
printf("timezone : %s\n", timezone->valuestring);
printf("timezone_offset : %s\n", timezone_offset->valuestring);
free(json_str);
cJSON_Delete(json);
}
}
int main(int argc, char const *argv[]) {
int tcp_socket = create_tcp_socket();
struct sockaddr_in server_addr = resolve_hostname(HOSTNAME);
connect_to_server(tcp_socket, server_addr);
send_http_request(tcp_socket, HOSTNAME, KEY);
char *response = receive_http_response(tcp_socket);
parse_json_response(response);
free(response); // 释放接收到的响应
close(tcp_socket); // 关闭套接字
return 0;
}