對Json文件的數據進行讀取與保存操作


准備:

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;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM