오브젝트나 연결된 오브젝트의 묶음(오브젝트 그래프)을 바이트 스트림으로 변환하는 과정
// MyGameInstance.h
struct FStudentData
{
FStudentData() {}
FStudentData(int32 InOrder, const FString& InName) : Order(InOrder), Name(InName) {}
friend FArchive& operator<<(FArchive& Ar, FStudentData& InStudentData)
{
Ar << InStudentData.Order;
Ar << InStudentData.Name;
return Ar;
}
int32 Order = -1;
FString Name = TEXT("홍길동");
};
UCLASS()
class UNREALSERIALIZATION_API UMyGameInstance : public UGameInstance
{
...
virtual void Init() override;
};
// MyGameInstance.cpp
void UMyGameInstance::Init()
{
Super::Init();
FStudentData RawDataSrc(16, TEXT("이득우"));
// 언리얼 프로젝트의 Saved 폴더
const FString SavedDir = FPaths::Combine(FPlatformMisc::ProjectDir(), TEXT("Saved"));
UE_LOG(LogTemp, Log, TEXT("저장할 파일 폴더 : %s"), *SavedDir);
{
const FString RawDataFileName(TEXT("RawData.bin"));
FString AbsolutePath = FPaths::Combine(*SavedDir, *RawDataFileName);
UE_LOG(LogTemp, Log, TEXT("저장할 파일 전체 경로 : %s"), *AbsolutePath);
FPaths::MakeStandardFilename(AbsolutePath);
UE_LOG(LogTemp, Log, TEXT("변경할 파일 전체 경로 : %s"), *AbsolutePath); // 깔끔한 파일 경로
// 데이터 저장
FArchive* RawFileWriterAr = IFileManager::Get().CreateFileWriter(*AbsolutePath);
if (RawFileWriterAr != nullptr)
{
//*RawFileWriterAr << RawDataSrc.Order;
//*RawFileWriterAr << RawDataSrc.Name;
*RawFileWriterAr << RawDataSrc; // << 연산자 오버로딩
RawFileWriterAr->Close(); // 전송 완료 후 파일 닫기
delete RawFileWriterAr;
RawFileWriterAr = nullptr
}
// 데이터 불러오기
FStudentData RawDataDest;
FArchive* RawFileReaderAr = IFileManager::Get().CreateFileReader(*AbsolutePath);
if (RawFileReaderAr != nullptr)
{
*RawFileReaderAr << RawDataDest; // Writer와 꼴은 같지만 거꾸로 흘러가서 RawDataDest로 불러옴
RawFileReaderAr->Close();
delete RawFileReaderAr;
RawFileReaderAr = nullptr;
UE_LOG(LogTemp, Log, TEXT("[RawData] 이름 %s 순번 %d"), *RawDataDest.Name, RawDataDest.Order); // 이득우 16
}
}
}
// Student.h
UCLASS()
class UNREALSERIALIZATION_API UStudent : public UObject
{
...
public:
virtual void Serialize(FArchive& Ar) override;
private:
UPROPERTY()
int32 Order;
UPROPERTY()
FString Name;
};
// Student.cpp
void UStudent::Serialize(FArchive& Ar)
{
Super::Serialize(Ar);
Ar << Order;
Ar << Name;
}
// MyGameInstance.h
UCLASS()
class UNREALSERIALIZATION_API UMyGameInstance : public UGameInstance
{
...
UPROPERTY()
TObjectPtr<class UStudent> StudentSrc;
};
// MyGameInstance.cpp
void UMyGameInstance::Init()
{
...
StudentSrc = NewObject<UStudent>();
StudentSrc->SetName(TEXT("이득우"));
StudentSrc->SetOrder(59);
{
const FString ObjectDataFileName(TEXT("ObjectData.bin"));
FString ObjectDataAbsolutePath = FPaths::Combine(*SavedDir, *ObjectDataFileName);
FPaths::MakeStandardFilename(ObjectDataAbsolutePath);
// 메모리에 먼저 저장
TArray<uint8> BufferArray; // 바이트 스트림, 직렬화를 위한 버퍼
FmemoryWriter MemoryWriterAr(BufferArray);
StudentSrc->Serialize(MemoryWriterAr); // 버퍼에 StudentSrc 데이터 기록
// 파일로 저장
if (TUniquePtr<FArchive> FileWriterAr = TUniquePtr<FArchive>(IFileManager::Get().CreateFileWriter(*ObjectDataAbsolutePath)))
{
// 스마트포인터를 사용했기 때문에 블럭을 벗어나면 FileWriterAr은 자동으로 메모리 해제
*FileWriterAr << BufferArray;
FileWriterAr->Close();
}
// 버퍼에 불러오기
TArray<uint8> BufferArrayFromFile;
if (TUniquePtr<FArchive> FileReaderAr = TUniquePtr<FArchive>(IFileManager::Get().CreateFileReader(*ObjectDataAbsolutePath)))
{
*FileReaderAr << BufferArrayFromFile;
FileReaderAr->Close();
}
// 버퍼의 데이터를 메모리에 전송
FMemoryReader MemoryReaderAr(BufferArrayFromFile);
UStudent* StudentDest = NewObject<UStudent>();
StudentDest->Serialize(MemoryReaderAr); // StudentDest로 불러옴
PrintStudentInfo(StudentDest, TEXT("ObjectData")); // 이득우 59
}
}
void PrintStudentInfo(const UStudent* InStudent, const FString& InTag)
{
UE_LOG(LogTemp, Log, TEXT("[%s] 이름 %s 순번 %d"), *InTag, *InStudent->GetName(), InStudent->GetOrder());
}
JavaScript Object Notation의 약자
웹 환경에서 서버와 클라이언트 사이에 데이터를 주고받을 때 사용하는 텍스트 기반 데이터 포맷
UnrealSerialzation.Build.cs 파일에 Json과 JsonUtilities 모듈 추가
// MyGameInstance.cpp
#include "JsonObjectConverter.h"
void UMyGameInstance::Init()
{
...
{
const FString JsonDataFileName(TEXT("StudentJsonData.txt"));
FString JsonDataAbsolutePath = FPaths::Combine(*SavedDir, *JsonDataFileName);
FPaths::MakeStandardFilename(JsonDataAbsolutePath);
TSharedRef<FJsonObject> JsonObjectSrc = MakeShared<FJsonObject>(); // Null이 아님을 보장
FJsonObjectConverter::UStructToJsonObject(StudentSrc->GetClass(), StudentSrc, JsonObjectSrc); // UStruct를 JsonObject로 변환 (UObject도 UStruct를 상속 받으므로)
// 파일로 저장
FString JsonOutString;
TSharedRef<TJsonWriter<TCHAR>> JsonWriterAr = TJsonWriterFactory<TCHAR>::Create(&JsonOutString);
if (FJsonSerializer::Serialize(JsonObjectSrc, JsonWriterAr))
{
FFileHelper::SaveStringToFile(JsonOutString, *JsonDataAbsolutePath);
}
// 파일 불러오기
FString JsonInString;
FFileHelper::LoadFileToString(JsonInString, *JsonDataAbsolutePath);
TSharedRef<TJsonReader<TCHAR>> JsonReaderAr = TJsonReaderFactory<TCHAR>::Create(JsonInString);
TSharedPtr<FJsonObject> JsonObjectDest; // Json으로 안만들어졌을 수 있으므로 포인터로 받기
if (FJsonSerializer::Deserialize(JsonReaderAr, JsonObjectDest))
{
UStudent* JsonStudentDest = NewObject<UStudent>();
if (FJsonObjectConverter::JsonObjectToUStruct(JsonObjectDest.ToSharedRef(), JsonStudentDest->GetClass(), JsonStudetnDest))
{
PrintStudentInfo(JsonStudentDest, TEXT("JsonData")); // 이득우 59
}
}
}
}