일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- Python
- intervals
- asgi
- FastAPI
- IAM
- event loop
- dockerfile
- leetcode
- docker
- ebs
- K8S
- EKS
- terraform
- AWS
- 자바스크립트
- Deployment
- github
- kernel
- POD
- asyncio
- EC2
- Kubernetes
- ansible
- WSGI
- Service
- Django
- elasticsearch
- 쿠버네티스
- IAC
- YAML
- Today
- Total
궁금한게 많은 개발자 노트
[ C++ ] jsoncpp - parsing json file 본문
간단하게 우선, JSON이란 ?
JavaScript Object Notation라는 의미의 축약어로 데이터를 저장하거나 전송할 때 사용되는 경량의 DATA 교환 형식
JSON은 데이터 포맷일 뿐이며 어떠한 통신 방법도, 프로그래밍 문법도 아닌 단순히 데이터 표현 방법일 뿐이다.
Key-Value형식으로 구성되어 있으며, JSON 각 Key-Value의 Type으로는
null, number, string, array, object, boolean을 사용
프로그램 내에서 json file을 작성하거나 json file을 읽어와 parsing한 후 사용해야 할 경우
cpp에서는 jsoncpp library를 사용하여 간편하게 작성 및 파싱을 할 수 있다.
https://github.com/open-source-parsers/jsoncpp
cpp의 경우 주로 Makefile이나 CMakeLists.txt를 사용하여 프로젝트를 빌드할텐데, CMake의 경우 아래와 같이
FindPkgConfig의 pkg_check_modules를 사용하여 jsoncpp를 사용하기 위한 linker flag, cflag를 가져와
CMAKE_CXX_FLAGS에 setting 및 target_link_libraries를 사용하여 executable에 link를 걸어준다.
include(FindPkgConfig)
pkg_check_modules(PKGS REQUIRED jsoncpp)
foreach(flag ${PKGS_CFLAGS})
set(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
endforeach(flag)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_CFLAGS}")
set(SRCS ...)
add_executable(${PROJECT_NAME} ${SRCS})
target_link_libraries(diagnostics_test ${PKGS_LDFLAGS})
CMakeLists.txt는 c/c++로 개발을 하다보면 계속해서 사용해야 하는데,
아직까지도 제대로 공부해보지 않아서 따로 정리를 한번 할 필요가 있을것 같다.
jsoncpp로 돌아와서, 아래와 같은 json format으로 파일을 생성하고, 그것을 읽어와 parsing해보려 한다.
{
"age" : 28,
"friends" : [
{
"age" : 25,
"name" : "Kim"
},
{
"age" : 30,
"name" : "Park"
}
],
"hasCar" : true,
"items" : [ "nootbook", "iphone", "apple-watch" ],
"name" : "Lee"
}
json document를 구성하는 type으로는 Json::Value를 사용한다. 위의 {"age" : 28} 과 같은 하나의 형태가 Json::Value로 구성되며,
전체 가장 바깥쪽을 구성하는 중괄호도 결국엔 하나의 Json::Value이며, 그 내부에 다른 Object(Json::Value)들이 포함되어 있다.
Json::Value는 key-value로 구성되어 있으며,
값을 입력하는 방식으로는 인덱스 연산자 []에 key를 입력하고, value를 대입하는 방식으로 사용한다.
위에 보이는 "firends"나 "items"처럼 List형태의 경우에는 append() method를 사용, Object를 추가하는 방식으로 구현.
Object를 중첩하는 경우에는 마찬가지로 상위 Object[Key] = SubObject형태로 넣어주면 된다.
#include <iostream>
#include <json/json.h> // using for json format
...
void print_json() {
Json::Value root; // 기본 json document
root["name"] = "Lee";
root["age"] = 28;
root["hasCar"] = true;
Json::Value items; // list를 위한 value
items.append("nootbook"); // append를 이용하여 추가
items.append("iphone");
items.append("apple-watch");
root["items"] = items;
Json::Value friends;
Json::Value tom;
tom["name"] = "Park";
tom["age"] = 30;
Json::Value jane; // 각 Object에 대해서도 append로 추가
jane["name"] = "Kim";
jane["age"] = 25;
friends.append(tom);
friends.append(jane);
root["friends"] = friends;
Json::StyledWriter writer; // json format으로 출력하기 위해 사용
str = writer.write(root);
std::cout << str << std::endl << std::endl;
}
위의 예제는 단순히 json document를 생성하고, 출력하는 방식의 예제이지만,
실제로 많이 사용되는 방식은 json file을 읽어오거나 json format으로 file을 작성하는 상황이 많을것이다.
이때 fstream library를 사용해 ifstream, ofstream으로 file 입출력을 한다.
#include <fstream> // using for json file write/read
#include <json/json.h>
void parsing_json() {
std::ifstream json_file(path, std::ifstream::binary);
// 생성하면서 바로 path와 open mode를 parameter로 전달 가능
std::ifstream json_file;
json_file.open(path, std::ifstream::in|std::ifstream::binary);
// 이처럼 open함수를 이용할 수 있으며, open mode는 | 연산자를 이용하여 여러 모드 가능
Json::Value root
json_file >> root;
json_file.close();
// open한 file에서 얻어온 내용을 root에 대입하여 Json::Value로 사용
...
// 원하는 수정을 거친 후 formating json
Json::StyledWriter writer;
str = writer.write(root);
// output to json file
std::ofstream output_file("output.json");
output_file << str;
output_file.close();
}
위의 예제에서는 ifstream을 사용하여 path에 있는 file을 원하는 open mode를 사용하여 읽어온 후,
원하는 작업을 거친 후 마지막에 수정내용을 "output.json"으로 출력하는 예제입니다.
json file을 읽어오는 방식에는 위처럼 ifstream에서 생성자를 호출하거나, open()을 사용하는 방법도 있지만,
jsoncpp에서 제공하는 method를 사용하는 방법도 있다. 아마 이방법이 좀 더 jsoncpp를 잘사용하는 느낌..?
#include <fstream>
#include <json/json.h>
#include <iostream>
void read_json() {
Json::CharReaderBuilder builder;
builder["collectComments"] = false;
Json::Value value;
Json::JSONCPP_STRING errs;
bool ok = parseFromStream(builder, std::cin, &value, &errs);
if (ok == true) {
// access json value ...
}
}
추가로 Json::Value를 사용하여 위에서 저장한 data들을 어떤방식으로 수정 및 접근하는지에 대해 알아보려한다.
void read_json() {
// 수정할때와 마찬가지로 가장 바깥쪽의 Json::Value의 Key에 접근시
// root[Key]로 value를 가져올 수 있다.
std::cout << "name : " << root["name"].asString() << std::endl;
std::cout << "age : " << root["age"].asInt() << std::endl;
// "friends", "items"와 같은 list를 접근할 경우에는 iterator를 사용
// .asString(), .asInt()를 사용하는 이유는, Json::Value에서 출력시 typecasting 필요
Json::Value friend_list = root["friends"];
for (Json::ValueIterator it = friend_list.begin(); it != friend_list.end(); it++) {
std::cout << "friend name : " << (*it)["name"].asString() << std::endl;
std::cout << "firend age : " << (*it)["age"].asInt() << std::endl;
}
// Vector와 비슷한 자료구조라고 생각하면 되는게, direct indexing도 가능하다.
for (int index = 0; indext < (int)friend_list.size(); index++) {
std::cout << "friend name : " << friend_list[index]["name"].asString() << std::endl;
std::cout << "firend age : " << friend_list[index]["age"].asInt() << std::endl;
// 아래 처럼 get() method를 사용하면, 만약 해당 Key가 없다면 2번째 인자를 return한다.
std::cout << "friend name : " << friend_list[index].get("name", "empty").asString() << std::endl;
std::cout << "firend age : " << friend_list[index].get("age", -1).asInt() << std::endl;
}
}
다음에 json file을 parsing할 필요가 있을때 잘 사용할 수 있을것 같다.
'Language' 카테고리의 다른 글
[ Github ] Commit message 작성법 (1) | 2020.07.10 |
---|---|
[ Github ] 자주 사용하는 Github 명령어 (0) | 2020.07.01 |
[ python ] 시스템 명령어 사용 (2) | 2020.06.18 |
[ C++ ] sprintf, snprintf의 차이 (0) | 2020.06.16 |
[ C++ ] 스마트 포인터 - auto_ptr, unique_ptr, shared_ptr, weak_ptr (0) | 2020.06.16 |