How to recursively get the child properties of the property?

Now I could get the property of the actor through unreal reflection.

But some of them are custom type or complicated type(not simple type), I want to parse these complicated property recursively to get their inner property which is simple type.

I have some problems with getting the child property of the property, the code is following:
I found that if Property refers to a complex type like FVector, the code will not be able to enter the for loop below.So I think there is problem with getting the child property

FString GetBasicTypePropertyValue(FProperty* Property, void* ValueAddress)
{	
	
	if (Property->IsA(FIntProperty::StaticClass()))
	{
		return FString::FromInt(*(int32*)ValueAddress);
	}
	else if (Property->IsA(FFloatProperty::StaticClass()))
	{
		return FString::SanitizeFloat(*(float*)ValueAddress);
	}
	else if (Property->IsA(FBoolProperty::StaticClass()))
	{
		return (*(bool*)ValueAddress) ? TEXT("True") : TEXT("False");
	}
	else if (Property->IsA(FStrProperty::StaticClass()))
	{
		return *(FString*)ValueAddress;
	}
    // complicated type, get the property
	UObject* ObjectValue = Property->ContainerPtrToValuePtr<UObject>(ValueAddress);
	FString ResultString;

	if (ObjectValue != nullptr)
	{
		UE_LOG(LogTemp, Error, TEXT("GetBasicTypePropertyValue get from the property :%s"), *Property->GetName());
		for (TFieldIterator<FProperty> PropertyIt(ObjectValue->GetClass()); PropertyIt; ++PropertyIt)
		{
			FProperty* ChildPropertyPtr = *PropertyIt;
			UE_LOG(LogTemp, Error, TEXT("GetBasicTypePropertyValue for PropertyName: %s, PropertyType: %s."), *ChildPropertyPtr->GetName(), *ChildPropertyPtr->GetCPPType());
			ResultString += GetBasicTypePropertyValue(ChildPropertyPtr, ChildPropertyPtr->ContainerPtrToValuePtr<void>(ValueAddress));
			return ResultString;
		}
	}

	return FString();

}

There are several types of “complex” properties and many of them are not UObjects, so getting their value as UObject* and iterating them via GetClass() is gonna go very wrong for those.

Your code is almost correct, for any property of type FObjectPropertyBase (or derived). However the UObject* value should be obtained like other primitives, not using ContainerPtrToValuePtr at this place, ie. in your style it would be
UObject* ObjectValue = *(UObject*)ValueAddress;

You need additional code to handle array properties, map properties, set properties and struct properties.

Also the code is cleaner (imo) when using cast-conditions, rather than IsA+c-style casts.
Here is a more complete example, though it’s still missing several property types :

static FString GetBasicTypePropertyValue(FProperty* Property, void* ValueAddress)
{
    if (auto IntProp = CastField<FIntProperty>(Property))
    {
        return FString::FromInt(IntProp->GetPropertyValue(ValueAddress));
    }
    else if (auto FloatProp = CastField<FFloatProperty>(Property))
    {
        return FString::SanitizeFloat(FloatProp->GetPropertyValue(ValueAddress));
    }
    else if (auto BoolProp = CastField<FBoolProperty>(Property))
    {
        return BoolProp->GetPropertyValue(ValueAddress) ? TEXT("True") : TEXT("False");
    }
    else if (auto StrProp = CastField<FStrProperty>(Property))
    {
        return StrProp->GetPropertyValue(ValueAddress);
    }
    else if (auto ObjProp = CastField<FObjectPropertyBase>(Property))
    {
        UObject* Obj = ObjProp->GetObjectPropertyValue(ValueAddress);
        if (Obj)
        {
            FString ResultString;
            UE_LOG(LogTemp, Error, TEXT("GetBasicTypePropertyValue get from the property :%s"), *Property->GetName());
            for (TFieldIterator<FProperty> PropertyIt(Obj->GetClass()); PropertyIt; ++PropertyIt)
            {
                FProperty* ChildProp = *PropertyIt;
                UE_LOG(LogTemp, Error, TEXT("GetBasicTypePropertyValue for PropertyName: %s, PropertyType: %s."), *ChildProp->GetName(), *ChildProp->GetCPPType());
                ResultString += GetBasicTypePropertyValue(ChildProp, ChildProp->ContainerPtrToValuePtr<void>(Obj));
            }
            return ResultString;
        }
        return TEXT("None");
    }
    else if (auto ArrayProp = CastField<FArrayProperty>(Property))
    {
        TArray<FString> Items;
        FScriptArrayHelper ArrayHelper(ArrayProp, ValueAddress);
        Items.Reserve(ArrayHelper.Num());
        for (int32 i = 0; i < ArrayHelper.Num(); i++)
        {
            Items.Emplace(GetBasicTypePropertyValue(ArrayProp->Inner, ArrayHelper.GetRawPtr(i)));
        }
        return FString::Printf(TEXT("(%s)"), *FString::Join(Items, TEXT(",")));
    }
    else if (auto StructProp = CastField<FStructProperty>(Property))
    {
        TArray<FString> Items;
        for (TFieldIterator<FProperty> PropertyIt(StructProp->Struct); PropertyIt; ++PropertyIt)
        {
            FProperty* ChildProp = *PropertyIt;
            FString ChildValue = GetBasicTypePropertyValue(ChildProp, ChildProp->ContainerPtrToValuePtr<void>(ValueAddress));
            Items.Emplace(FString::Printf(TEXT("%s=%s"), *ChildProp->GetName(), *ChildValue));
        }
        return FString::Printf(TEXT("(%s)"), *FString::Join(Items, TEXT(",")));
    }
    else
    {
        UE_LOG(LogTemp, Warning, TEXT("Unhandled property type: %s"), *Property->GetCPPType());
        // generic
        FString Value;
        Property->ExportText_Direct(Value, ValueAddress, ValueAddress, nullptr, PPF_UseDeprecatedProperties);
        return Value;
    }
}
2 Likes

Thank you for taking the time to help me out with the code. Your assistance was invaluable and greatly appreciated!

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.