매우 간단한 json 래퍼 클래스
json을 써 볼까 싶은 생각에 찾아 보니 여러가지가 있지만 rapidjson이 괜찮은 듯해서 선택했다. 그런데 문제는 항상 반복하게 될 파일 열기. 그래서 이 부분만 간단히 처리할 클래스를 만들어 보았다. 즉 큰 기대는 금물.
입출력할 파일에 관한 정보를 굳이 멤버로 두지 않은 이유는 다음과 같다. 파일을 열고 읽는 멤버 함수를 만들고 보니 파일에 쓰는 멤버 함수도 있으면 좋을 것 같아 추가했다. 생성자에서 파일 이름을 받도록 하고 각 읽기/쓰기 멤버 함수에서 그대로 쓰면 괜찮겠는데, 생각해 보니 정보를 읽은 파일이 아니라 다른 파일에 기록할 수 있으면 더 좋을 것 같다. 결국 읽기/쓰기 멤버 함수도 파일을 받는 것과 안 받는 것 두 가지를 만들 수 있겠다. 즉 생성자에서 파일을 받으면 read()
/write()
를 사용해 읽고 쓰면 되고, 생성자에서 받지 않으면 read(file)
/write(file)
를 사용하면 된다.
그런데 생성자에서 파일을 받고 read()
로 읽은 후 write(file)
로 기록하거나 그 반대 상황, 또는 생성자와 멤버를 의도와 달리 섞어 쓴다면 클래스에 멤버로 있는 파일 정보는 어떻게 처리하는 게 좋을까. 그래서 내린 결론은 읽고 쓸 때 직접 지정하기로 했다. 헷갈릴 염려도 없고 가장 직관적이지 않을까.
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 31 32 33 34 35 36 37 38 39 40 41 |
#ifndef JSON_H_ #define JSON_H_ #include "tstring.h" #include "thirdparty/rapidjson/document.h" class Json { public: using Document = rapidjson::Document; using SizeType = rapidjson::SizeType; using Value = rapidjson::Value; Json() {} ~Json() {} /** * Read a json file. * Read from the file specified in the parameter. * @param file a json file included full path * @return If succeeded return true, otherwise false */ bool read(std::tstring const& file); /** * Write a json file. * Write to the file specified in the parameter. * @param file a json file included full path * @return If succeeded return true, otherwise false */ bool write(std::tstring const& file); Document& document() {return document_;} private: FILE* fp_ = nullptr; rapidjson::Document document_; }; #endif |
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
#include "json.h" #include "thirdparty/rapidjson/filereadstream.h" #include "thirdparty/rapidjson/filewritestream.h" #include "thirdparty/rapidjson/prettywriter.h" using std::tstring; using rapidjson::Document; using rapidjson::FileReadStream; using rapidjson::FileWriteStream; using rapidjson::PrettyWriter; const int kReadBufferSize = 65536; bool Json::read(std::tstring const& file) { errno_t err = ::_tfopen_s(&fp_, file.c_str(), _T("rb")); if (err) { return false; } char readBuffer[kReadBufferSize]; FileReadStream readStream(fp_, readBuffer, _countof(readBuffer)); bool result = !document_.ParseStream(readStream).HasParseError(); ::fclose(fp_); return result; } bool Json::write(std::tstring const& file) { errno_t err = ::_tfopen_s(&fp_, file.c_str(), _T("wb")); if (err) { return false; } char writeBuffer[kReadBufferSize]; FileWriteStream writeStream(fp_, writeBuffer, _countof(writeBuffer)); PrettyWriter<FileWriteStream> writer(writeStream); document_.Accept(writer); ::fclose(fp_); return true; } |
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 |
#include <iostream> #include "json.h" int _tmain(int argc, _TCHAR* argv[]) { std::tstring fileName = _T("sample.json"); Json json; json.read(fileName.c_str()); Json::Document& doc = json.document(); Json::Value& gem = doc["a"]; for (Json::SizeType i = 0; gem.Size() != i; ++i) { std::cout << gem[i]["id"].GetInt() << std::endl; std::cout << gem[i]["name"].GetString() << std::endl; std::cout << gem[i]["max"].GetDouble() << std::endl; gem[i]["name"] = "mod"; } json.write(fileName.c_str()); return 0; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
{ "a": [ { "id": 1, "name": "abc", "max": 324.22 }, { "id": 2, "name": "bcd", "max": 43242 } ], "b": [ { "id": 1, "name": "bbc" }, { "id": 2, "name": "bac" } ] } |
rapidjson 관련 파일 경로를 적절히 바꾸고 컴파일하면 std::tstring
때문에 오류가 발생할 텐데 간단히 std::string
으로 바꾸면 된다. 실행하면 파일을 읽어 화면 출력 후 배열 a
에 대한 각 요소 이름을 mod
로 바꿔 다시 저장한다.
그런데 딱히 프로그램 외부에서 정보를 계속 바꿔 가며 사용할 일이 없다 보니 이걸 써야 할까 말아야 할까 아직도 고민 중인 게 함정.