UE5, BlueprintでJSONデータを取り扱う

# BlueprintにはJSONを直接扱う機能がない？

Blueprint上でお手軽に扱う方法はないようで、C++用に用意されているJSONを扱うためのライブラリを用いて、UObjectを拡張するなどが必要なようです

Gitに公開されているコードを参考に、ほぼほぼコピーですが、必要最低限の機能を実装してみました

## 環境
| ソフトウェア | バージョン | 
|:-----------|------------:|
| MacOS       | 10.15.7        | 
| UnrealEngine | 5.0.0 |
| Visual Studio Code |  1.61.2 |
| Xcode | 12.4 |
| .NET SDK |5.0.402|

## コード
プロジェクト名は仮に`JsonTest`としています

```C++:BlueprintJsonObjectLite.h
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "BlueprintJsonObjectLite.generated.h"

/**
 * 
 */
UCLASS()
class JSONTEST_API UBlueprintJsonObject : public UObject
{
	GENERATED_BODY()

private:
	TSharedPtr<FJsonObject> mJsonObject;

	void SetSharedPointer(TSharedPtr<FJsonObject> JsonObject)
	{
		mJsonObject = JsonObject;
	}

public:
	UBlueprintJsonObject();

	TSharedPtr<FJsonObject> ToSharedPointer();

	static UBlueprintJsonObject *FromSharedPointer(TSharedPtr<FJsonObject> JsonObject);

	UFUNCTION(BlueprintPure, meta = (DisplayName = "Create JSON Object", Keywords = "Json Create Object", ShortTooltip = "Creates an empty Json Object"), Category = "Json|Object")
	static UBlueprintJsonObject *Create();

	UFUNCTION(BlueprintPure, meta = (DisplayName = "Parse Object String", Keywords = "Json Parse Object String", ShortTooltip = "Parses a Json Object String ({\"some\": \"example\"})"), Category = "Json|Object")
	static bool Parse(FString JsonString, UBlueprintJsonObject *&JsonObject);

	UFUNCTION(BlueprintPure, meta = (DisplayName = "Stringify Object", Keywords = "Json Stringify Object", AdvancedDisplay = "PrettyPrint", ShortTooltip = "Converts a Json Object to a single string"), Category = "Json|Object")
	FString Stringify(bool PrettyPrint = false);

	//
	// Has, Get Methods

	UFUNCTION(BlueprintPure, meta = (DisplayName = "Has Boolean", Keywords = "Json Has Boolean", ShortTooltip = "Does the Json Object have this key and is it a boolean?"), Category = "Json|Object")
	bool HasBoolean(FString Key);

	UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Boolean", Keywords = "Json Get Boolean", ShortTooltip = "Gets a certain boolean from the Json Object"), Category = "Json|Object")
	bool GetBoolean(FString Key, bool& Value);

	UFUNCTION(BlueprintPure, meta = (DisplayName = "Has Integer", Keywords = "Json Has Integer", ShortTooltip = "Does the Json Object have this key and is it an integer?"), Category = "Json|Object")
	bool HasInteger(FString Key);

	UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Integer", Keywords = "Json Get Integer", ShortTooltip = "Gets a certain integer from the Json Object"), Category = "Json|Object")
	bool GetInteger(FString Key, int32& Value);

	UFUNCTION(BlueprintPure, meta = (DisplayName = "Has Float", Keywords = "Json Has Float", ShortTooltip = "Does the Json Object have this key and is it a float?"), Category = "Json|Object")
	bool HasFloat(FString Key);

	UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Float", Keywords = "Json Get Float", ShortTooltip = "Gets a certain float from the Json Object"), Category = "Json|Object")
	bool GetFloat(FString Key, float& Value);

	UFUNCTION(BlueprintPure, meta = (DisplayName = "Has String", Keywords = "Json Has String", ShortTooltip = "Does the Json Object have this key and is it a string?"), Category = "Json|Object")
	bool HasString(FString Key);

	UFUNCTION(BlueprintPure, meta = (DisplayName = "Get String", Keywords = "Json Get String", ShortTooltip = "Gets a certain string from the Json Object"), Category = "Json|Object")
	bool GetString(FString Key, FString& Value);

	UFUNCTION(BlueprintPure, meta = (DisplayName = "Has Object", Keywords = "Json Has Object", ShortTooltip = "Does the Json Object have this key and is it another Json Object?"), Category = "Json|Object")
	bool HasObject(FString Key);

	UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Object", Keywords = "Json Get Object", ShortTooltip = "Gets a certain Json Object from the Json Object"), Category = "Json|Object")
	bool GetObject(FString Key, UBlueprintJsonObject *&Value);

	UFUNCTION(BlueprintPure, meta = (DisplayName = "Has Array", Keywords = "Json Has Array", ShortTooltip = "Does the Json Object have this key and is it a Json Array?"), Category = "Json|Object")
	bool HasArray(FString Key);

	UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Array", Keywords = "Json Get Array", ShortTooltip = "Gets a certain Json Array from the Json Object without specifying a type"), Category = "Json|Object")
	bool GetArray(FString Key, TArray<UBlueprintJsonObject *> &Values);
};

```

```C++:BlueprintJsonObjectLite.cpp
// Fill out your copyright notice in the Description page of Project Settings.


#include "BlueprintJsonObjectLite.h"

UBlueprintJsonObject::UBlueprintJsonObject()
{
    mJsonObject = MakeShareable(new FJsonObject);
}

TSharedPtr<FJsonObject> UBlueprintJsonObject::ToSharedPointer()
{
    return mJsonObject;
}

UBlueprintJsonObject *UBlueprintJsonObject::FromSharedPointer(TSharedPtr<FJsonObject> JsonObject)
{
    UBlueprintJsonObject *Object = NewObject<UBlueprintJsonObject>();
    Object->SetSharedPointer(JsonObject);
    return Object;
}

// Blueprint

UBlueprintJsonObject *UBlueprintJsonObject::Create()
{
    return NewObject<UBlueprintJsonObject>();
}

bool UBlueprintJsonObject::Parse(FString JsonString, UBlueprintJsonObject *&JsonObject)
{
    TSharedPtr<FJsonObject> Object;
    TSharedRef<TJsonReader<> > Reader = TJsonReaderFactory<>::Create(JsonString);
    if (!FJsonSerializer::Deserialize(Reader, Object))
        return false;

    JsonObject = UBlueprintJsonObject::FromSharedPointer(Object);
    return true;
}

FString UBlueprintJsonObject::Stringify(bool PrettyPrint)
{
    FString JsonString;
    if (PrettyPrint)
    {
        TSharedRef<TJsonWriter<TCHAR, TPrettyJsonPrintPolicy<TCHAR> > > Writer = TJsonWriterFactory<TCHAR, TPrettyJsonPrintPolicy<TCHAR> >::Create(&JsonString);
        FJsonSerializer::Serialize(mJsonObject.ToSharedRef(), Writer);
    }
    else
    {
        TSharedRef<TJsonWriter<TCHAR, TCondensedJsonPrintPolicy<TCHAR> > > Writer = TJsonWriterFactory<TCHAR, TCondensedJsonPrintPolicy<TCHAR> >::Create(&JsonString);
        FJsonSerializer::Serialize(mJsonObject.ToSharedRef(), Writer);
    }
    return JsonString;
}

bool UBlueprintJsonObject::HasBoolean(FString Key) {
	return mJsonObject->HasTypedField<EJson::Boolean>(Key);
}

bool UBlueprintJsonObject::GetBoolean(FString Key, bool& Value) {
	if (!HasBoolean(Key))
		return false;
	return mJsonObject->TryGetBoolField(Key, Value);
}

bool UBlueprintJsonObject::HasInteger(FString Key) {
	if (!mJsonObject->HasTypedField<EJson::Number>(Key))
		return false;

	double val;
	bool success = mJsonObject->TryGetNumberField(Key, val);
	if (!success)
		return false;

	double intpart;
	return std::modf(val, &intpart) == 0.0;
}

bool UBlueprintJsonObject::GetInteger(FString Key, int32& Value) {
	if (!HasInteger(Key))
		return false;
	return mJsonObject->TryGetNumberField(Key, Value);
}

bool UBlueprintJsonObject::HasFloat(FString Key) {
	return mJsonObject->HasTypedField<EJson::Number>(Key);
}

bool UBlueprintJsonObject::GetFloat(FString Key, float& Value) {
	if (!HasFloat(Key))
		return false;
	double val;
	bool success = mJsonObject->TryGetNumberField(Key, val);
	if (success)
		Value = val;
	return success;	
}

bool UBlueprintJsonObject::HasString(FString Key) {
	return mJsonObject->HasTypedField<EJson::String>(Key);
}

bool UBlueprintJsonObject::GetString(FString Key, FString& Value) {
	if (!HasString(Key))
		return false;
	return mJsonObject->TryGetStringField(Key, Value);
}

bool UBlueprintJsonObject::HasObject(FString Key) {
	return mJsonObject->HasTypedField<EJson::Number>(Key);
}

bool UBlueprintJsonObject::GetObject(FString Key, UBlueprintJsonObject*& Value) {
	const TSharedPtr<FJsonObject>* JsonObject;
	bool Success = mJsonObject->TryGetObjectField(Key, JsonObject);
	if (Success)
		Value = UBlueprintJsonObject::FromSharedPointer(*JsonObject);

	return Success;
}

bool UBlueprintJsonObject::HasArray(FString Key)
{
    return mJsonObject->HasTypedField<EJson::Array>(Key);
}

bool UBlueprintJsonObject::GetArray(FString Key, TArray<UBlueprintJsonObject *> &Values)
{
    if (!HasArray(Key))
        return false;

    const TArray<TSharedPtr<FJsonValue> > *vals;
    bool success = mJsonObject->TryGetArrayField(Key, vals);
    if (!success)
        return false;

    for (TSharedPtr<FJsonValue> Value : *vals)
    {
        TSharedPtr<FJsonObject> JsonObject = Value->AsObject();
        Values.Add(UBlueprintJsonObject::FromSharedPointer(JsonObject));
    }
    return true;
}
```

用意されているライブラリ、`JsonUtilities`では、同じJSONでも`JsonObject`と`JsonValue`に分けて管理されているようです

が、二つ分のコードを書くのがイヤだったので、`JsonObject`一本でコントロールできるように変更しました

## さいごに

ほぼコピペです

JsonValueの管理を回避するために、Array型の戻り値を調整したところだけががんばったところです


## 参考
Git : [Cl1608Ho/JsonBlueprint](https://github.com/Cl1608Ho/JsonBlueprint)