对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