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;
}

posted @ 2024-06-11 23:52  banon  阅读(3)  评论(0编辑  收藏  举报