[Visual C++] 서울시 열린데이터 광장 Open API 이용하기 – 3. json 파싱 (jsoncpp)

오늘은 저번 글에 이어서 json을 파싱하는 법을 알아보겠습니다.

우선 json 파싱을 위해 아래 파일을 다운로드 합니다.

압축 해제 후에 파일들을 어제 만들었던 프로젝트에 추가합니다. jsoncpp는 깃허브에서도 다운 받을수 있습니다
근데 이게 잘 안되서 제가 찾은 것중에 MFC에서 되게 제가 일부 수정했습니다.

아무튼 프로젝트 추가하시고 아래와 같이 헤더파일을 include 합니다.

#include "json.h"

어제 코드에 이어서 아래와 같이 입력합니다.

std::string strJson = acRead;

Json::Value jsRoot;
Json::Reader jsReader;
jsReader.parse( strJson, jsRoot );

이 글에서는 테스트를 이 링크로 할 예정입니다.

해당 링크 데이터에서 아래와 같은 json이 Read 됩니다.(제가 임의로 보기좋게 수정했습니다.)

{"rentBikeStatus":
	{"list_total_count":5,
	 "RESULT":
		{"CODE":"INFO-000",
		 "MESSAGE":"정상 처리되었습니다."},
		 "row":[
			{"rackTotCnt":"7",
			 "stationName":"101. (구)합정동 주민센터",
			 "parkingBikeTotCnt":"1",
			 "shared":"0",
			 "stationLatitude":"37.54956055",
			 "stationLongitude":"126.90575409",
			 "stationId":"ST-3"},
			 
			{"rackTotCnt":"22",
			 "stationName":"102. 망원역 1번출구 앞",
			 "parkingBikeTotCnt":"16",
			 "shared":"9",
			 "stationLatitude":"37.55564880",
			 "stationLongitude":"126.91062927",
			 "stationId":"ST-4"},

			{"rackTotCnt":"16",
			 "stationName":"103. 망원역 2번출구 앞",
			 "parkingBikeTotCnt":"6",
			 "shared":"13",
			 "stationLatitude":"37.55495071",
			 "stationLongitude":"126.91083527",
			 "stationId":"ST-5"},

			{"rackTotCnt":"15",
			 "stationName":"104. 합정역 1번출구 앞",
			 "parkingBikeTotCnt":"13",
			 "shared":"0",
			 "stationLatitude":"37.55062866",
			 "stationLongitude":"126.91498566",
			 "stationId":"ST-6"},

			{"rackTotCnt":"7",
			 "stationName":"105. 합정역 5번출구 앞",
			 "parkingBikeTotCnt":"5",
			 "shared":"14",
			 "stationLatitude":"37.55000687",
			 "stationLongitude":"126.91482544",
			 "stationId":"ST-7"}]}}

만약 rentBikeStatus의 list_total_count 값을 알고싶으면 아래와 같이 코딩하면 됩니다.

Json::Value jsRoot;
Json::Reader jsReader;
jsReader.parse( strJson, jsRoot );

int iCount;
if ( jsRoot.get( "rentBikeStatus", "" ).isObject() ) // Object 확인
{
	iCount = jsRoot.get( "rentBikeStatus", "" ).get( "list_total_count", "" ).asInt();
}

CString sVale;
sVale.Format( _T("%d"), iCount );
::AfxMessageBox( sVale );

asString으로 값을 받아오기 전 항상 isObject로 확인해야 합니다.

연속된 데이터 row는 아래와 같이 코딩하시면 됩니다.

// row 데이터 받아오기
Json::Value jsRow = jsRoot.get( "rentBikeStatus", "" )["row"];
for ( int iLoop = 0 ; iLoop < iCount ; iLoop++ )
{
	std::string strStationName = jsRow[iLoop].get( "stationName", "" ).asString(); // 값 읽기
	CString sStationName = CA2W( strStationName.c_str(), CP_UTF8 ); // Ansi 데이터 Wide Character로 변환
	::AfxMessageBox( sStationName );
}

URL에 요청해서 json파일을 읽어와 파싱하는 전체 코드는 아래와 같습니다.

#include <afxinet.h>
void CMFCApplication1Dlg::OnBnClickedButtonCInternetSession()
{
	UpdateData(TRUE);

	// 세션이 연결된 이후, 웹 사이트의 Html 정보가 변경된 사항을 가져올 경우 
	CInternetSession session( NULL, 1, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, INTERNET_FLAG_DONT_CACHE );

	try
	{
		// 세션 클래스를 이용하여 열기
		CInternetFile *pinetFile = (CInternetFile *)session.OpenURL( m_sUrl );

		if ( NULL != pinetFile )
		{
			char acRead[40960] = { 0, }; // return 값이 길 수 있으므로 충분히 입력
			memset( acRead, 0, sizeof( acRead ) );

			// json 읽는 부분
			UINT uiTotalRead = 0;
			int iIdex = 0;
			do 
			{
				// 데이터를 1024씩 읽어 온다.
				uiTotalRead = pinetFile->Read( &acRead[iIdex], 1024 );
				if ( uiTotalRead <= 0 )
					break;

				iIdex += uiTotalRead;
			} while ( uiTotalRead > 0 );

			// 읽은 json 표시를 위한 부분
			std::string strJson = acRead;

			Json::Value jsRoot;
			Json::Reader jsReader;
			jsReader.parse( strJson, jsRoot );

			int iCount;
			if ( jsRoot.get( "rentBikeStatus", "" ).isObject() ) // Object 확인
			{
				iCount = jsRoot.get( "rentBikeStatus", "" ).get( "list_total_count", "" ).asInt();
			}
			CString sVale;
			sVale.Format( _T("%d"), iCount );
			::AfxMessageBox( sVale );

			// row 데이터 받아오기
			Json::Value jsRow = jsRoot.get( "rentBikeStatus", "" )["row"];
			for ( int iLoop = 0 ; iLoop < iCount ; iLoop++ )
			{
				std::string strStationName = jsRow[iLoop].get( "stationName", "" ).asString();
				CString sStationName = CA2W( strStationName.c_str(), CP_UTF8 );
				::AfxMessageBox( sStationName );
			}

			pinetFile->Close(); // Html 파일을 닫는다.			
			session.Close(); // 세션 종료

			delete pinetFile; // Html 파일 읽기에 사용한 객체를 파괴

			m_sResult = CA2W( strJson.c_str(), CP_UTF8 );
		}

	}
	catch ( CInternetException *e )
	{
		// 에러
	}

	UpdateData( FALSE );
}

다음 글에서는 XML파싱을 주제로 글을 써보겠습니다.

위 코드는 샘플이니 디버깅을 걸어 확인하시고, 읽어오는 것을 직접 해보시길 바랍니다.