TIL - 14(rapidjson)

jh Seo·2024년 8월 12일

rapidjson을 이용해 json파일 IO

item클래스

io를 해줄 아이템 클래스를 작성해봤다.

class FItem {

public:
	FItem() = default;
    FItem(const FItem& others) = default;
    FItem(const std::string_view& _name, float _limitLevel, bool _isUsable)
    : 
        itemName(_name)
        , limitLevel(_limitLevel)
        , isUsable(_isUsable)
    {

    }
	~FItem() = default;
    void Save(rapidjson::Value& InOutVal, rapidjson::Document::AllocatorType& InAlloc) {
        if (itemName.empty()) {
            _ASSERT(false);
        }

        //만약 여기서 새로 string형 value를 할당을 안하고, 그대로 itemName을 넣어주게되면
        //해당 객체의 수명이 문자열의 수명이므로 json문서가 잘못 참조하게될수도있음.
        //따라서 새로 문자열을 할당해서 넣어줌
        rapidjson::Value itemNameString(rapidjson::kStringType);
        itemNameString.SetString(itemName.data(),InAlloc);

        InOutVal.AddMember("itemName", itemNameString, InAlloc);
        InOutVal.AddMember("limitLevel", limitLevel, InAlloc);
        InOutVal.AddMember("isUsable", isUsable, InAlloc);
    }
    void Load(rapidjson::Value& val)
    {
        if (val.HasMember("itemName")) {
            const char* tmpName = val["itemName"].GetString();
            itemName = tmpName;
        }
        else
        {
            _ASSERT(false);
            itemName = "DefaultName";
        }
        if (val.HasMember("limitLevel"))
            limitLevel = val["limitLevel"].GetFloat();

        if (val.HasMember("isUsable"))
            isUsable = val["isUsable"].GetBool();
    }

private:
	std::string itemName;
	float limitLevel;
	bool isUsable;
};

멤버변수로는 itemName, limitLevel, isUsable 간단히 이렇게 세 가지를 저장했다.

Save함수

void Save(rapidjson::Value& InOutVal, rapidjson::Document::AllocatorType& InAlloc) {
        if (itemName.empty()) {
            _ASSERT(false);
        }

        //만약 여기서 새로 string형 value를 할당을 안하고, 그대로 itemName을 넣어주게되면
        //해당 객체의 수명이 문자열의 수명이므로 json문서가 잘못 참조하게될수도있음.
        //따라서 새로 문자열을 할당해서 넣어줌
        rapidjson::Value itemNameString(rapidjson::kStringType);
        itemNameString.SetString(itemName.data(),InAlloc);

        InOutVal.AddMember("itemName", itemNameString, InAlloc);
        InOutVal.AddMember("limitLevel", limitLevel, InAlloc);
        InOutVal.AddMember("isUsable", isUsable, InAlloc);
    }

rapidjson::Value&형으로 인자를 받아온다.
InOutVal에 addmember함수를 이용해 key와 value를 저장해준다.
addmember함수를 사용하기 위해선 인자로 넘어온 allocator가 필요하다.

여기서 멤버변수인 itemName을 바로 사용하지 않는 이유는
해당 문자열의 수명이 객체의 수명과 동일하므로 위험하기 때문이다.
안전한 참조를 위해 rapidjson::Value형으로 새로 문자열을 할당해 넣어줬다.

Load함수

void Load(rapidjson::Value& val)
{
    if (val.HasMember("itemName")) {
        const char* tmpName = val["itemName"].GetString();
        itemName = tmpName;
    }
    else
    {
        _ASSERT(false);
        itemName = "DefaultName";
    }
    if (val.HasMember("limitLevel"))
        limitLevel = val["limitLevel"].GetFloat();

    if (val.HasMember("isUsable"))
        isUsable = val["isUsable"].GetBool();
}

역시나 인자로 rapidjson::Value&타입으로 val변수를 받아온다.

원하는 value를 얻기 위해 key값으로 값을 받아온 후,
value 타입에 맞게 GetString(), GetBool(), GetFloat()등을 사용하면 된다.

json파일 Write하는 부분

 using uint = unsigned int;
 constexpr uint itemNumbers = 5;
 std::vector<FItem> Items;
 Items.reserve(itemNumbers);

 //items벡터에 아이템들 넣어주기
 Items.emplace_back( "HpPotion" , 1,true );
 Items.emplace_back( "MpPotion" , 1,true );
 Items.emplace_back( "Sword" , 1,false );
 Items.emplace_back( "Shield" , 1,false );
 Items.emplace_back( "Hammer" , 1,false );

 rapidjson::Document Doc(rapidjson::kObjectType);
 rapidjson::Value Array(rapidjson::kArrayType);
 
 //아이템들을 Save함수를 통해 인자로 넣어준 rapidjson::Value에 할당해주고, 
 // kArrayType인 Array에 pushback해준다.
 for (FItem& elem : Items)
 {
     rapidjson::Value ItemValue(rapidjson::kObjectType);
     elem.Save(ItemValue, Doc.GetAllocator());

     Array.PushBack(ItemValue,Doc.GetAllocator());
 }

 //위 Array를 Doc에  key값(ItemInfo)의 value로 넣어준다.
 Doc.AddMember("ItemInfo", Array, Doc.GetAllocator());
 
 //객체 정보를 string형으로 
 rapidjson::StringBuffer Buffer;
 rapidjson::Writer<rapidjson::StringBuffer> writer(Buffer);
 Doc.Accept(writer);
 std::string Json(Buffer.GetString(), Buffer.GetSize());

 //ofstream형식으로 파일 생성 후, string형으로 넣어준다.
 std::ofstream File("item.json");
 File << Json;

아이템들을 생성해주고, kArrayType형식의 배열 Array에 아이템 객체들을 PushBack함수를 통해
넣어준다.

"itemInfo" 라는 key를 생성후 , 해당 key값의 value로 Array를 넣어준다.

마지막에 ofstream형식을 이용해 파일 생성 후, string값을 작성하면 된다.

json파일 read하는 부분

       std::vector<FItem> Items;

       //item.json파일을 열고 내부 string값들을 전부 읽어온다.
       std::ifstream File("item.json");
       std::string json ="";
       std::string TempLine;
       while (std::getline(File,TempLine))
       {
           json += TempLine;
       }

       //rapidjson::Document Doc변수를 생성하고 Parse함수를 통해 위 json 정보를 파싱한다.
       rapidjson::Document Doc(rapidjson::kObjectType);
       Doc.Parse(json.data());

       //iteminfo 키값에 아이템 정보 배열인 array를 저장했으므로 iteminfo 검색
       bool bItemInfo = Doc.HasMember("ItemInfo");
       if (bItemInfo)
       {
           // 배열 가져와서 순회하며 item의 load함수 호출하여 만들어진 객체들 벡터에 push_back
           rapidjson::Value& Array = Doc["ItemInfo"];
           if (Array.IsArray())
           {
               rapidjson::SizeType size = Array.Size();
               for (size_t i = 0; i < size; i++)
               {
                   FItem item;
                   rapidjson::Value& value = Array[i];
                   item.Load(value);

                   Items.push_back(item);
               }
           }
       }
   }
profile
코딩 창고!

0개의 댓글