【转载】UE4 C++ 相关 Json 操作

1,913 阅读7分钟

读取 Json 数据,解析 Json 数组

版权声明:本文为CSDN博主「虎冯河」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:blog.csdn.net/qq_39934403…

Json 格式示例:

{
    "function": "Macro",
    "params": {
        "behaviors": [{
                "behavior": {
                    "function": "1566",
                    "params": {
                        "duration": 6.37,
                        "filePath": "http://17/api/15"
                    }
 
                },
                "start_time": 0
            },
            {
                "behavior": {
                    "function": "416546",
                    "params": {
                        "duration": 6.37,
                        "filePath": "http://175/915"
                    }
                },
                "start_time": 7.0
            }
        ]
    }
}

使用内置的 json 模块之前,在项目的 Build.cs 文件中,包含一下 JsonJsonUtilities 模块。

image.png

解析 Json 的函数

void ParseJsonObject(const FString messageStr)
{
    TSharedPtr<FJsonObject> jsonObject;                                                        // FJsonObject 类型
    TSharedRef<TJsonReader<TCHAR>> jsonReader = TJsonReaderFactory<TCHAR>::Create(messageStr); // 类型转换
    bool isSe = FJsonSerializer::Deserialize(jsonReader, jsonObject);                          // 序列化,返回 FJsonObject 类型数据
    if (isSe)                                                                                  // 判断一下是否转换成功
    {
        FString func = jsonObject->GetStringField("function");                                    // function 字符串参数
        FString content = jsonObject->GetStringField("content");                                  // content 字符串参数
        TSharedPtr<FJsonObject> paramsObject = jsonObject->GetObjectField("params");              // params 参数 FJsonObject 类型
        TArray<TSharedPtr<FJsonValue>> behaviorsArray = paramsObject->GetArrayField("behaviors"); // params下的 behaviors 数组参数

        for (int i = 0; i < behaviorsArray.Num(); i++) // 遍历数组
        {

            FString timeStr = behaviorsArray[i]->AsObject()->GetStringField("start_time");                   // behaviors 数组下的 behavior 的开始时间
            float startTime = FCString::Atof(*timeStr);                                                      // 开始时间 float 类型
            TSharedPtr<FJsonObject> behaviorObj = behaviorsArray[i]->AsObject()->GetObjectField("behavior"); // behaviors 数组下的 behavior 参数
            FString beFuncStr = behaviorObj->GetStringField("function");                                     // behavior 下的 function 字符串参数
            TSharedPtr<FJsonObject> beParam = behaviorObj->GetObjectField("params");                         // behavior 下的 params 参数
            FString filePathStr = beParam->GetStringField("filePath");                                       // params 参数下的 filePath 字符串参数
        }
    }
}

存储 Json 数据并输出字符串

作者:埃罗芒阿Sensal

原文链接: www.bilibili.com/read/cv1007…

一. 介绍

Json 是一种数据对象,数据由键值对组成.

  1. 简单的 Json 数据对象:
//{"键1":"值1","键2":"值2",....}
{"Name":"Tom","Age":"18","Sex":"man"}
  1. Json 对象作为键值对的值
// 班长信息:Json对象作为值
{"MonitorInfo":{"Name":"Tom","Age":"20","Sex":"man"}}
  1. Json 对象数组
[
  // 第一个对象
 {"Name":"Tom","Age":"18","Sex":"man"},
  // 第二个对象
 {"Name":"Jerry","Age":"17","Sex":"man"},
  // 第三个对象
 {"Name":"Lily","Age":"20","Sex":"woman"}
]
  1. Json 对象数组作为键值对的值
{
  // 一班
  "ClassNum":"1",
  //老师叫张三
  "TeacherName":"ZhangSan",
  // 学生数组
  "Students":
  [
      // 学生1
     {"Name":"Tom","Age":"18","Sex":"man"},
      // 学生2
     {"Name":"Jerry","Age":"17","Sex":"man"},
      // 学生3
     {"Name":"Lily","Age":"20","Sex":"woman"}
 
    ]
}

二. UE4 Json 使用说明

使用 Json 模块需要在 Build.cs 中将 Json 模块导入

PublicDependencyModuleNames.AddRange(
  new string[]
    {
       "Json"
    }
  );

1. FJsonObject 说明

FJsonObject 在 UE4 里表示一个 Json 对象,用于存储 Json 数据

(1) 初始化方式

/**
 * 因为 FJsonObject 并非 UObjec t的派生类,无法自动进行垃圾回收,所以使用共享指针防止内存泄漏
 * 下面定义一个空的 Json 对象
 */

TSharedPtr<FJsonObject> Object;

(2) FJsonObject 存储数据的几种形式

① 将常规类型作为值存储
// 1. 存储数据节点
 
// (1) SetStringField(FString key,FString value) 存储一个 FString 字段
Object->SetStringField(TEXT("Name"),TEXT("Tom"));
 
/**
 * (2) SetNumberField(FString key,double value) 存储一个浮点型字段
 *ps: 并没有 SetIntegerField , 存储整形也可以使用这个
 */
Object->SetNumberField(TEXT("Age"),18);
 
// (3) SetBoolField(FString key,bool value) 存储一个 bool 型字段
Object->SetBoolField(TEXT("IsMan"),true);

// 截止到次, Object 内的数据为 {"Name":"Tom", "Age":18, "IsMan":true}

  • 实机测试代码如下

image.png

② 将 FJsonObject 作为值存储
//创建一个JsonObject
TSharedPtr<FJsonObject>Object= MakeShareable(new FJsonObject);
//填充字段信息
Object->SetStringField(TEXT("Name"), TEXT("Tom"));
Object->SetNumberField(TEXT("Age"), 18);
Object->SetBoolField(TEXT("IsMan"), true);
 
//创建另一个JsonObject作为上面的JsonObject中的一个字段的值
TSharedPtr<FJsonObject>obj = MakeShareable(new FJsonObject);
obj->SetNumberField(TEXT("weight"), 130);
obj->SetNumberField(TEXT("height"), 180);
obj->SetStringField(TEXT("Hobby"), TEXT("Game"));
 
//将obj作为Object中的Info字段的值
Object->SetObjectField(TEXT("Info"), obj);
  • 实机测试代码

image.png

③.将 TArray<TSharedPtr<FJsonValue>> 作为值存储

结构大致如下

{
  "field1":"xx",
  "field2":"yy",
  "fields":
      [
      // 切记! 数组中的元素必须要是字段都一致的 Json 对象,所有字段名都得是一样
      {"Name":"tom","Age":18},
      {"Name":"jerry","Age":17}
   ]
}
/**
 *SetArrayField(FString key,TArray>value)
 *存储一个数组作为某个字段的值
 */
 
// 1. 创建一个空的 FJsonValue 数组
TArray<TSharedPtr<FJsonValue>> JsonValueArray;

// 2. 创建两个 FJsonObject 作为数组元素
TSharedPtr<FJsonObject> vobj1 = MakeShareable(new FJsonObject);
vobj1->SetStringField(TEXT("familymember"), TEXT("Jerry"));

TSharedPtrv<FJsonObject> vobj2 = MakeShareable(new FJsonObject);
vobj2->SetStringField(TEXT("familymember"), TEXT("John"));

// 3. 将两个 FJsonObject 转化为 FJsonValue ,存储到数组中,到此数组填充完毕
JsonValueArray.Add(MakeShareable(new FJsonValueObject(vobj1)));
JsonValueArray.Add(MakeShareable(new FJsonValueObject(vobj2)));

Object->SetArrayField(TEXT("Famliy"), JsonValueArray);
 
/**
 *截止到此 Object 内的信息
 *{
 *  "Name":"Tom",
 *  "Age":18,
 *  "IsMan":true,
 *  "Family":
 *       [
 *        {"familymember":"Jerry"},
 *        {"familymember":"john"}
 *       ]
 *}
 *SetArrayField 要比其他的用法稍稍复杂一些,注意理解
 */
  • 实机测试代码

image.png

(3)FJsonObject获取数据的方式

① 判断是否拥有某个字段

image.png

判断传入的字段是否存在, 在你想要查询某个字段的数据但是不确定 Json 中是否有这个字段时,可以先判断一下

image.png

image.png

进阶查询用法, 不光可以 判断字段是否存在 , 还能 判断字段的类型是否一致

② TryGetXXField
/**
 * FieldName: 要查询的字段名
 * OutValue: 该参数需要传入一个引用,用于接收查询到的结果
 * 返回一个 bool 值,查询到数据返回 true ,查询不到返回 false
 */
TryeGetXXField(FString FieldName, XXType &OutValue);

// 假设一个简单的 json 对象(伪代码)
TSharedPtr<FJsonObject> obj = {"Name" : "Tom", "Age" : 18, "IsMan" : true};

FString OutString;
obj->TryGetStringField("Name", OutString); // OutString=Tom;

bool bIsMan = false;
obj->TryGetBoolField("IsMan", bIsMan); // bIsMan=true;

int32 age = 0;
// TryGetNumberField 可以支持获取 double int32 uint32 int64 类型的数值
obj->TryGetNumberField("Age", age); // age=18;

// obj 还能够获得 FJsonObject 和 TArray<FJsonValue> 数组
③ GetXXField
  • GetNumberField(获取浮点型数值) return double,
  • GetBoolField(获取 bool 型数值) return bool ,
  • GetArrayField(获取 FJsonValue 数组作为数值) return TArray<TSharedPtr>,
  • GetObjectField(获取 FJsonObject 作为数值) return TSharedPtr.

说明:

使用 TryGetGet 都可以用来获取数据,如果 Json 数据的字段是明确的,那么可以直接使用 Get 来获取. 如果不确定,可以使用 TryGet , 根据需求选择即可

(4) 逆序列化(Deserialize) FJsonObject

常用于将一个 FString 类型的 Json 数据存入到 FJsonObject 中

/**
 *JsonStr 是 UE4 中的 Json 数据
 *注意!!! 在 UE4 中需要使用\"(反斜杠+双引号,转义字符) 来替代"(双引号)
 *看到 \" 直接在心里替换成 " 即可
 */
FString JsonStr = TEXT("{\"Name\":\"Tom\",\"Age\":\"18\",\"Sex\":\"man\"}");

// 使用工厂类,把 FString 格式的 Json 数据存到创建出来的 Json Reader 中
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonStr);

// 创建一个空的 JsonObject(Json 对象,因为其基类不是 UObject , 为了防止内存泄漏,使用共享指针)
TSharedPtr<FJsonObject> Object;

// FJsonSerializer 通过 Deserialize(逆序列化)拿 Reader 中存储的数据来填充空的 Json 对象
FJsonSerializer::Deserialize(Reader, Object);

//如果逆序列化成功, Json 对象 IsValid 将返回 true , 然后我们就可以通过他读取数据了
if (Object.IsValid())
{
    // Json 对象通过 GetStringField,传入字段名(键),获得字符串类型的值
    FString Name = Object->GetStringField(TEXT("Name"));
    // Json 对象通过 GetIntegerField ,传入字段名(键),获得整形的值
    int32 Age = Object->GetIntegerField(TEXT("Age"));
}
  • 实机测试代码

image.png

(5) 序列化(Serialize) FJsonObject

常用于将 FJsonObject 中的数据写入到 FString

// 准备一个用于序列化的 FJsonObject
TSharedPtr<FJsonObject> Obj = MakeShareable(new FJsonObject);
Obj->SetStringField("Name", "Tom");
Obj->SetNumberField("Age", 18);
Obj->SetBoolField("IsMan", true);

// 创建一个空的 FString 用于接收序列化后的 json 数据
FString OutString;
TSharedRef<TJsonWriter<TCHAR, TCondensedJsonPrintPolicy<TCHAR>>> Writer = TJsonWriterFactory<TCHAR, TCondensedJsonPrintPolicy<TCHAR>>::Create(&OutString);

// Serializer 将 Json 对象中的数据通过 Writer 写入到 outstring 中
bool bResult = FJsonSerializer::Serialize(Obj.ToSharedRef(), Writer);
if (bResult)
{
    UE_LOG(LogTemp, Warning, TEXT("serialize succeeded!! outstring:%s"), *OutString);
}

image.png

可以看到空的字符串被写入了数据

2. FJsonValue 说明

FJsonValue 在 UE4 中作为 Json 数据键值对中的值存在

(1) 初始化方式

FJsonValue 针对每一种 Json 值类型都进行了派生,引擎不提倡我们直接使用 FJsonValue 基类,而是提倡我们根据值类型来选择不同的派生类

派生类如下

1.FJsonValueString
 
// 初始化方式
TSharedPtr<FJsonValue> StrVal = MakeShareable(new FJsonValueString(""));
 
2.FJsonValueNumber
 
// 初始化方式
TSharedPtr<FJsonValue> numVal = MakeShareable(new FJsonValueNumber(""));
 
3.FJsonValueBoolean
 
// 初始化方式
TSharedPtr<FJsonValue> boolVal = MakeShareable(new FJsonValueBoolean(false));
 
4.FJsonValueObject
 
// 初始化方式
TSharedPtr<FJsonObject> Obj = MakeShareable(new FJsonObject);
TSharedPtr<FJsonValue> ObjVal = MakeShareable(new FJsonValueObject(Obj));
 
5. FJsonValueArray
 
// 初始化方式
TArray<TSharedPtr<FJsonValue>> InArray;
TSharedPtr<FJsonValue>ArrayVal = MakeShareable(new FJsonValueArray(InArray));
// 还有一些不太常用的派生类, 想了解更多可以去 Engine\Source\Runtime\Json\Public\Dom\JsonValue.h 中进行查看

(2) 常用节点

  • ① AsXXX

根据选择的类型(FJsonValueXXX), 可以使用对应的 AsXXX 来获取数据内容

比如 FJsonValueString 可以使用 AsString 返回存储的字符串

  • ② TryGetXXX

AsXXX 一样,根据类型来决定使用具体的 TryGetXXX 节点获取数据

3. 示例代码

// 创建主体 Json 对象
TSharedPtr<FJsonObject> TomObj = MakeShareable(new FJsonObject);

// 填充常规类型字段数据
TomObj->SetStringField("Name", "Tom");
TomObj->SetNumberField("Age", 18);
TomObj->SetBoolField("IsMan", true);

// 创建一个子 Json 对象作为值
TSharedPtr<FJsonObject> InfoObj = MakeShareable(new FJsonObject);
InfoObj->SetNumberField("Height", 180);
InfoObj->SetNumberField("Weight", 70);

TomObj->SetObjectField("Info", InfoObj);

// 创建两个子 Json 对象,数组的元素
TArray<TSharedPtr<FJsonValue>> ValueArray;
TSharedPtr<FJsonObject> FriendObj1 = MakeShareable(new FJsonObject);
TSharedPtr<FJsonObject> FriendObj2 = MakeShareable(new FJsonObject);

// 填充子对象数据
FriendObj1->SetStringField("Name", "jerry");
FriendObj1->SetNumberField("IsMan", true);
FriendObj2->SetStringField("Name", "lily");
FriendObj2->SetNumberField("IsMan", false);
ValueArray.Add(MakeShareable(new FJsonValueObject(FriendObj1)));
ValueArray.Add(MakeShareable(new FJsonValueObject(FriendObj2)));

TomObj->SetArrayField("Friends", ValueArray);
FString OutString;
TSharedRef<TJsonWriter<TCHAR, TCondensedJsonPrintPolicy<TCHAR>>> Writer = TJsonWriterFactory<TCHAR, TCondensedJsonPrintPolicy<TCHAR>>::Create(&OutString);
bool bResult = FJsonSerializer::Serialize(TomObj.ToSharedRef(), Writer);
if (bResult)
{
    UE_LOG(LogTemp, Warning, TEXT("serialize succeeded!! outstring:%s"), *OutString);
}

image.png