Unrecognized type 'FVariant' when used in a UObject class

I’m still new to C++ and Unreal but I come from a C# background. I’m trying to implement 2 functions to serialize and deserialize a TMap<FString,FVariant> into a TArray and back.
The problem is I get this compile error when I attempt to build:

|Error||Unrecognized type ‘FVariant’ - type must be a UCLASS, USTRUCT, UENUM, or global delegate

This is in my .h file where I am defining these functions

UFUNCTION(BlueprintCallable, Category = “Conversion Helpers”)
static TMap<FString, FVariant> DeserializeByteData(TArray byteData);

UFUNCTION(BlueprintCallable, Category = "Conversion Helpers")
	static TArray<uint8> SerializeMapData(TMap<FString, FVariant> mapData);

Is FVariant even usable in this way? Essentially I want to make a function that I can call in a blueprint that will handle converting a TMap<FString, AnyDataType> to a byte array, after some research I found out about FVariant and thought it was exactly what I needed, but for some reason I can’t compile this code. What am I doing wrong and is there possibly another way to achieve this?

You cannot use FVariant in a UFUNCTION or UPROPERTY context because that class hasn’t been exposed to Unreals reflection system by either UCLASS or USTRUCT.

You can create your own wrapper USTRUCT but you’d have to write additional functions for working with the wrapped FVariant in Blueprint (e.g. custom setter, getter and make functions). Edit: Forgot to mention there’s also the StructUtils experimental plugin that already works similar to this.

Of course you could also extend/modify the engine to add support for actual TVariant<> properties but that’s a much bigger task.

Edit: Ignore the following, I somehow missed that you wouldn’t be able to have different types for each of the map values. I’ll leave it there for now so you may get an idea what can be done with wildcards. The process for creating the functions to work with an FVariant wrapper USTRUCT would be very similar to that.

You could instead create the functions with a wildcard parameter and then use the property system to serialize/deserialize.

// disclaimer: untested, uncompiled code
UFUNCTION(BlueprintCallable, CustomThunk, meta=(MapParam = "MapData"), Category = "Conversion Helpers")
static TArray<uint8> SerializeMapData(TMap<FString, int32>& MapData);

DECLARE_FUNCTION(execSerializeMapData)
{
	Stack.MostRecentProperty = nullptr;
	Stack.StepCompiledIn<FMapProperty>(nullptr);
	void* MapAddr = Stack.MostRecentPropertyAddress;
	const FMapProperty* const MapProperty = CastField<FMapProperty>(Stack.MostRecentProperty);
	if (!MapProperty || !CastField<FStrProperty>(MapProperty->KeyProp))
	{
		Stack.bArrayContextFailed = true;
		return;
	}
	
	P_FINISH;
	P_NATIVE_BEGIN;
	if(MapAddr)
	{
		FMemoryWriter Writer(*(TArray<uint8>*)RESULT_PARAM, true);
		FScriptMapHelper MapHelper(MapProperty, MapAddr);
		int32 Size = MapHelper.Num();
		for(int32 Index = 0; Size; ++Index)
		{
			if(MapHelper.IsValidIndex(Index))
			{
				// TODO: also serialize the key string?
				MapProperty->ValueProp->SerializeItem(FStructuredArchiveFromArchive(Writer).GetSlot(), MapHelper.GetValuePtr(Index), nullptr);
				--Size;
			}
		}
	}
	P_NATIVE_END
}
1 Like