Exercise:JSON解析

banon發表於2024-06-11

練習:利用某些平臺(聚合 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;
}

相關文章