准備:
PlayerInfo.json:
[
{
"MaxHp": 10
},
{
"Hp": 3
},
{
"Mode": "Crazy"
},
{
"Inventory": [
{
"0": "Sword"
},
{
"1": "Shield"
},
{
"2": "Foot"
}
]
}
]
過程:
創建一個C++空項目。
把PlayerInfo.json放到項目的Content\Data目錄下。
打開JH.Build.cs文件,為項目添加Json模塊:
using UnrealBuildTool; public class JH : ModuleRules { public JH(ReadOnlyTargetRules Target) : base(Target) { PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" }); PrivateDependencyModuleNames.AddRange(new string[] { }); PublicDependencyModuleNames.AddRange(new string[] { "Json", "JsonUtilities" }); } }
創建一個繼承於藍圖函數庫類。
DataHandle.h:
#pragma once #include "CoreMinimal.h" #include "Kismet/BlueprintFunctionLibrary.h" //使用Json相關的操作需要Json.h #include "Json.h" #include "DataHandle.generated.h" /** * */ //模式枚舉 UENUM(BlueprintType) enum class EMode : uint8 { Easy = 0, Normal = 1, Crazy = 2, }; UCLASS() class JH_API UDataHandle : public UBlueprintFunctionLibrary { GENERATED_BODY() public: //根據文件名與文件路徑讀取Json數據 UFUNCTION(BlueprintCallable, Category = Json) static void LoadDataFromJson(const FString& FileName, const FString& FilePath, float& MaxHp, float& Hp, EMode& Mode, TArray<FString>& Inventory); //把MaxHp、Hp、Mode、Inventory存入指定目錄下的文件里 UFUNCTION(BlueprintCallable, Category = Json) static void RecordDataToJson(const FString& FileName, const FString& FilePath, float MaxHp, float Hp, EMode Mode, TArray<FString> Inventory); private: //打印錯誤信息 static void Debug(const FString& Message); //在指定枚舉類型中尋找該值是否存在,存在則返回其枚舉值 template<typename TEnum> static TEnum GetEnumFromString(const FString& Name, const FString& Value); //把指定枚舉類型的值,作為字符串返回 template<typename TEnum> static FString GetEnumAsString(const FString& Namw, const TEnum& Value); }; template<typename TEnum> inline static TEnum UDataHandle::GetEnumFromString(const FString& Name, const FString& Value) { const UEnum* EnumPtr = FindObject<UEnum>(ANY_PACKAGE, *Name, true); if (!EnumPtr) { Debug(FString("Enum[") + Name + FString("] can't be found!")); return TEnum(0); } return (TEnum)EnumPtr->GetIndexByName(FName(*FString(Value))); } template<typename TEnum> FString UDataHandle::GetEnumAsString(const FString& Name, const TEnum& Value) { const UEnum* EnumPtr = FindObject<UEnum>(ANY_PACKAGE, *Name, true); if (!EnumPtr) { Debug(FString("Enum[") + Name + FString("] can't be found!")); return FString(" "); } return EnumPtr->GetNameStringByIndex((int32)Value); }
DataHandle.cpp:
#include "DataHandle.h" void UDataHandle::LoadDataFromJson(const FString& FileName, const FString& FilePath, float& MaxHp, float& Hp, EMode& Mode, TArray<FString>& Inventory) { /*根據文件路徑把文件數據保存到FString中*/ FString LoadResult; FString Mode_Str; if (!FileName.IsEmpty()) { FString Path = FPaths::ProjectContentDir() + FilePath + FileName; if (FPaths::FileExists(Path)) { if (FFileHelper::LoadFileToString(LoadResult, *Path)) { /*進行解析原始數據*/ TArray<TSharedPtr<FJsonValue>> ParsedData; //TJsonReaderFactory<TCHAR>的Create()方法會創建並返回一個TJsonReader<TCHAR>類型的值 TSharedRef<TJsonReader<TCHAR>> JsonReader = TJsonReaderFactory<TCHAR>::Create(LoadResult); //對原始數據進行反序列化操作,把結果放入到解析數據中 /* * 對象序列化是一個用於將對象狀態轉換為字節流的過程,可以將其保存到磁盤文件中或通過網絡發送到任何其他程序。 * 從字節流創建對象的相反的過程稱為反序列化。 * 以某種存儲形式使自定義對象持久化。 **/ if (FJsonSerializer::Deserialize(JsonReader, ParsedData)) { /*從反序列化完成的數據中提取各個屬性值*/ MaxHp = ParsedData[0]->AsObject()->GetNumberField(FString("MaxHp")); Hp = ParsedData[1]->AsObject()->GetNumberField(FString("Hp")); Mode_Str = ParsedData[2]->AsObject()->GetStringField(FString("Mode")); TArray<TSharedPtr<FJsonValue>> ItemArray = ParsedData[3]->AsObject()->GetArrayField(FString("Inventory")); for (int i = 0; i < ItemArray.Num(); i++) { FString Item = ItemArray[i]->AsObject()->GetStringField(FString::FromInt(i)); Inventory.Add(Item); } //把模式字符串轉換為模式枚舉類型 Mode = GetEnumFromString<EMode>(FString("EMode"), Mode_Str); } else { Debug(FString("Failed to Deseriazlize!")); } } else { Debug(FString("Failed to load file! --- ") + Path); } } else { Debug(FString("File does not exist!-- - ") + Path); } } else { Debug(FString("FileName can't be empty!")); } } void UDataHandle::RecordDataToJson(const FString& FileName, const FString& FilePath, float MaxHp, float Hp, EMode Mode, TArray<FString> Inventory) { //把模式從枚舉轉換成字符串 const FString Mode_Str = GetEnumAsString(FString("EMode"), Mode); //外層的{} TSharedPtr<FJsonObject> BaseObject = MakeShareable(new FJsonObject); //外層的[] TArray<TSharedPtr<FJsonValue>> BaseValue; //{"MaxHp":MaxHp} TSharedPtr<FJsonObject> MaxHpObject = MakeShareable(new FJsonObject); MaxHpObject->SetNumberField("MaxHp", MaxHp); TSharedPtr<FJsonValueObject> MaxHpValue = MakeShareable(new FJsonValueObject(MaxHpObject)); //{"Hp":Hp} TSharedPtr<FJsonObject> HpObject = MakeShareable(new FJsonObject); HpObject->SetNumberField("Hp", Hp); TSharedPtr<FJsonValueObject> HpValue = MakeShareable(new FJsonValueObject(HpObject)); //{"Mode":Mod} TSharedPtr<FJsonObject> ModeObject = MakeShareable(new FJsonObject); ModeObject->SetStringField("Mode", Mode_Str); TSharedPtr<FJsonValueObject> ModeValue = MakeShareable(new FJsonValueObject(ModeObject)); //背包字符串數組指針 TArray<FString>* InventoryPtr = &Inventory; //里層的[] TArray<TSharedPtr<FJsonValue>> ItemArray; for (int i = 0; i < InventoryPtr->Num(); ++i) { //{"0":Sword} //{"1":Food} // ... TSharedPtr<FJsonObject> ItemObject = MakeShareable(new FJsonObject); ItemObject->SetStringField(FString::FromInt(i), (*InventoryPtr)[i]); TSharedPtr<FJsonValueObject> ItemValue = MakeShareable(new FJsonValueObject(ItemObject)); ItemArray.Add(ItemValue); } //{"Inventory":[...]} TSharedPtr<FJsonObject> InventoryObject = MakeShareable(new FJsonObject); InventoryObject->SetArrayField("Inventory", ItemArray); TSharedPtr<FJsonValueObject> InventoryValue = MakeShareable(new FJsonValueObject(InventoryObject)); //外層的[ {"MaxHp":10}, {"Hp":Hp}, ... ] BaseValue.Add(MaxHpValue); BaseValue.Add(HpValue); BaseValue.Add(ModeValue); BaseValue.Add(InventoryValue); //外層的{"T",外層的[ ... ]} BaseObject->SetArrayField("T", BaseValue); FString Data_Str; if (BaseObject.IsValid() && BaseObject->Values.Num() > 0) { //把整個原始數據序列化,並寫入字符串 TSharedRef<TJsonWriter<TCHAR>> JsonWriter = TJsonWriterFactory<TCHAR>::Create(&Data_Str); FJsonSerializer::Serialize(BaseObject.ToSharedRef(), JsonWriter); //移除開頭字符串:{"T", Data_Str.RemoveAt(0, 8); //移除末尾字符串:} Data_Str.RemoveFromEnd(FString("}")); if (!Data_Str.IsEmpty()) { if (!FileName.IsEmpty()) { FString AbsPath = FPaths::ProjectContentDir() + FilePath + FileName; //把字符串保存為指定目錄下的文件 if (FFileHelper::SaveStringToFile(Data_Str, *AbsPath)) { Debug(FString("Save data successfully!")); } else { Debug(FString("Save " + AbsPath + FString(" failed!"))); } } else { Debug("FileName cannot be empty!"); } } else { Debug("Data that needs to be saved is empty!"); } } else { Debug(FString("Fail to be JsonObject!")); } } void UDataHandle::Debug(const FString& Message) { if (GEngine) { GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, Message); } }
編譯后生成的藍圖節點:

首先測試加載數據節點:

測試結果:

接着測試保存數據節點:
Macro_LoadData使用的就是第一次測試的方法。

測試結果:

補充:
class JSON_API FJsonObject
{
public:
TMap<FString, TSharedPtr<FJsonValue>> Values;
{
public:
TMap<FString, TSharedPtr<FJsonValue>> Values;
