84.2 JSON 파일 읽기

이제 본격적으로 parson을 사용하여 JSON 문서를 읽어보겠습니다. 다음 내용을 main.c 파일이 있는 폴더에 example.json으로 저장합니다(파일은 GitHub 저장소의 Unit 84/84.2/parson/parson 폴더에 들어있습니다).

example.json

{
  "Title": "Inception",
  "Year": 2010,
  "Runtime": 148,
  "Genre": "Sci-Fi",
  "Director": "Christopher Nolan",
  "Actors": [
    "Leonardo DiCaprio",
    "Joseph Gordon-Levitt",
    "Ellen Page",
    "Tom Hardy",
    "Ken Watanabe"
  ],
  "imdbRating": 8.8,
  "KoreaRelease": true
}
  • JSON_Value * json_parse_file(const char *filename)
    • JSON 파일을 읽어서 파싱한 뒤 JSON_Value 포인터를 반환합니다.
  • JSON_Object * json_value_get_object(const JSON_Value *value)
    • JSON_Value에서 JSON_Object 포인터를 얻습니다.
  • const char * json_object_get_string(const JSON_Object *object, const char *name)
    • JSON 객체(JSON_Object)에서 키에 해당하는 문자열을 가져옵니다.
  • double json_object_get_number(const JSON_Object *object, const char *name)
    • JSON 객체에서 키에 해당하는 숫자를 가져옵니다.
  • JSON_Array * json_object_get_array(const JSON_Object *object, const char *name)
    • JSON 객체에서 키에 해당하는 배열의 포인터를 얻습니다.
  • size_t json_array_get_count(const JSON_Array *array)
    • JSON 배열의 요소 개수를 구합니다.
  • const char * json_array_get_string(const JSON_Array *array, size_t index)
    • JSON 객체에서 키에 해당하는 배열 중 인덱스를 지정하여 문자열을 가져옵니다.
  • int json_object_get_boolean(const JSON_Object *object, const char *name)
    • JSON 객체에서 키에 해당하는 불 값을 가져옵니다.
  • void json_value_free(JSON_Value *value)
    • JSON_Value에 할당된 동적 메모리를 해제합니다.

다음과 같이 parson 함수를 사용하여 초기화, 사용, 해제의 순서로 JSON 파일을 쓰게 됩니다.

그림 84‑9 parson 함수로 JSON 파일 읽기

먼저 parson의 함수를 사용하려면 #includeparson.h 헤더 파일을 포함합니다.

#include <stdio.h>
#include "parson.h"    // parson.h 헤더 파일 포함

이제 json_parse_file 함수에 example.json 파일을 지정하여 파일의 내용을 파싱합니다. parson은 JSON을 JSON_ValueJSON_Object 두 가지로 분리해서 처리하는데 json_parse_file 함수는 JSON_Value 포인터를 반환합니다. 따라서 JSON_Value에서 값을 꺼내려면 json_value_get_object 함수를 사용하여 JSON_Object 포인터를 얻습니다.

int main ()
{
    JSON_Value *rootValue;
    JSON_Object *rootObject;

    /* 초기화 */
    rootValue = json_parse_file("example.json");      // JSON 파일을 읽어서 파싱
    rootObject = json_value_get_object(rootValue);    // JSON_Value에서 JSON_Object를 얻음

보통 JSON 문서에서 처음 시작하는 {를 root object라고 부릅니다.

json_object_get_string 함수로 객체에서 키에 해당하는 문자열을 가져오고, json_object_get_number 함수로 객체에서 키에 해당하는 숫자를 가져오면 됩니다. 여기서 json_object_get_number 함수는 double형으로 값을 반환하므로 실수로 사용하고 싶으면 그대로 출력하면 되고, 정수로 사용하고 싶으면 앞에 (int)를 붙여서 자료형 변환을 해주면 됩니다.

    /* 사용 */
    // 객체에서 키에 해당하는 문자열을 가져옴
    printf("Title: %s\n", json_object_get_string(rootObject, "Title"));

    // 객체에서 키에 해당하는 숫자를 가져옴
    printf("Year: %d\n", (int)json_object_get_number(rootObject, "Year"));
    printf("Runtime %d\n", (int)json_object_get_number(rootObject, "Runtime"));

    // 객체에서 키에 해당하는 문자열을 가져옴
    printf("Genre: %s\n", json_object_get_string(rootObject, "Genre"));
    printf("Director: %s\n", json_object_get_string(rootObject, "Director"));

배열을 사용할 때는 먼저 json_object_get_array 함수로 객체에서 키에 해당하는 배열의 포인터(JSON_Array *)를 가져옵니다. 그리고 나서 반복문으로 반복할 때는 json_array_get_count 함수를 사용하면 배열의 요소 개수만큼 반복할 수 있습니다. 반복문 안에서는 json_array_get_string 함수로 배열의 문자열을 가져오면 됩니다.

    printf("Actors:\n");
    // 객체에서 키에 해당하는 배열을 가져옴
    JSON_Array *array = json_object_get_array(rootObject, "Actors");
    for (int i = 0; i < json_array_get_count(array); i++)     // 배열의 요소 개수만큼 반복
    {
        // 배열에 인덱스를 지정하여 문자열을 가져옴
        printf("  %s\n", json_array_get_string(array, i));
    }

객체에서 불 값을 가져올 때는 json_object_get_boolean 함수를 사용하면 됩니다.

    // 객체에서 키에 해당하는 숫자를 가져옴
    printf("imdbRating: %f\n", json_object_get_number(rootObject, "imdbRating"));

    // 객체에서 키에 해당하는 불 값을 가져옴
    printf("KoreaRelease: %d\n", json_object_get_boolean(rootObject, "KoreaRelease"));

모든 출력이 끝났으면 json_value_free 함수를 사용하여 동적 메모리를 해제합니다(rootValue를 해제하면 안에 들어있는 rootObject까지 함께 해제됩니다).

    /* 해제 */
    json_value_free(rootValue);    // JSON_Value에 할당된 동적 메모리 해제

    return 0;
}

다음은 전체 소스 코드입니다(파일은 GitHub 저장소의 Unit 84/84.2/parson/parson 폴더에 들어있습니다).

main.c

#include <stdio.h>
#include "parson.h"    // parson.h 헤더 파일 포함

int main()
{
    JSON_Value *rootValue;
    JSON_Object *rootObject;

    /* 초기화 */
    rootValue = json_parse_file("example.json");      // JSON 파일을 읽어서 파싱
    rootObject = json_value_get_object(rootValue);    // JSON_Value에서 JSON_Object를 얻음
    
    /* 사용 */
    // 객체에서 키에 해당하는 문자열을 가져옴
    printf("Title: %s\n", json_object_get_string(rootObject, "Title"));

    // 객체에서 키에 해당하는 숫자를 가져옴
    printf("Year: %d\n", (int)json_object_get_number(rootObject, "Year"));
    printf("Runtime %d\n", (int)json_object_get_number(rootObject, "Runtime"));

    // 객체에서 키에 해당하는 문자열을 가져옴
    printf("Genre: %s\n", json_object_get_string(rootObject, "Genre"));
    printf("Director: %s\n", json_object_get_string(rootObject, "Director"));

    printf("Actors:\n");
    // 객체에서 키에 해당하는 배열을 가져옴
    JSON_Array *array = json_object_get_array(rootObject, "Actors");
    for (int i = 0; i < json_array_get_count(array); i++)     // 배열의 요소 개수만큼 반복
    {
        // 배열에 인덱스를 지정하여 문자열을 가져옴
        printf("  %s\n", json_array_get_string(array, i));
    }

    // 객체에서 키에 해당하는 숫자를 가져옴
    printf("imdbRating: %f\n", json_object_get_number(rootObject, "imdbRating"));

    // 객체에서 키에 해당하는 불 값을 가져옴
    printf("KoreaRelease: %d\n", json_object_get_boolean(rootObject, "KoreaRelease"));

    /* 해제 */
    json_value_free(rootValue);    // JSON_Value에 할당된 동적 메모리 해제

    return 0;
}

Visual Studio에서 Ctrl+F5 키를 눌러서 프로그램을 실행하면 다음과 같이 JSON 문서를 파싱한 내용이 출력됩니다.

실행 결과

Title: Inception
Year: 2010
Runtime 148
Genre: Sci-Fi
Director: Christopher Nolan
Actors:
  Leonardo DiCaprio
  Joseph Gordon-Levitt
  Ellen Page
  Tom Hardy
  Ken Watanabe
imdbRating: 8.800000
KoreaRelease: 1

이처럼 기존에 구현되어 있는 JSON 파서를 이용하면 좀 더 체계적이고 효율적으로 JSON 문서를 처리할 수 있습니다.