Getting blueprint assets with the asset registry in packaged builds?

I need to get the UClass and the CDO of every blueprint in a specific folder that is derived from a class UMyClass.
In order to do this, I’m using the asset registry to load all UBlueprints in the folder.
Here is a simplified version of the code I’m using:

const FAssetRegistryModule& assetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
assetRegistryModule.Get().SearchAllAssets(true);
 
FARFilter filter;
filter.PackagePaths.Add(FName("/Game/GameContent/MyFolder"));
filter.bRecursivePaths = true;
filter.ClassPaths.Add(UBlueprint::StaticClass()->GetClassPathName());
 
TArray<FAssetData> bpAssets;
assetRegistryModule.Get().GetAssets(filter, bpAssets);
 
for (const auto& bpAsset : bpAssets)
{
	const UBlueprint* blueprint = Cast<UBlueprint>(bpAsset.GetAsset());
	TSubclassOf<UMyClass> bpClass = TSubclassOf<UMyClass>{blueprint->GeneratedClass};
	const UMyClass* bpCDO = bpClass->GetDefaultObject<UMyClass>();
	// Do stuff with the class and the CDO
}

This works fine in the editor, but fails in packaged builds (apparently the bpAssets array does get populated, but the cast to UBlueprint* inside the for loop seems to fail).
From what I understand, UBlueprints are replaced with UBlueprintGeneratedClasses in packaged builds, so I’m assuming this is the issue? I’m not sure how to rewrite this code to make it work both in the editor and in packaged builds, though (adding UBlueprintGeneratedClass::StaticClass()->GetClassPathName() to the filter’s class paths and casting to UBlueprintGeneratedClass* in the loop did not seem to solve the problem).
Any help would be appreciated. Thanks in advance!

Yeah, UBlueprint doesn’t exist in packaged builds.

I used this for the class:

  UClass* TargetSearchClass = GIsEditor ? UBlueprint::StaticClass() : UBlueprintGeneratedClass::StaticClass();

And when casting in your loop, I do this:

    UClass* CompiledClass = nullptr;

    if (GIsEditor)
    {
      // Editor path
      UBlueprint* BlueprintAsset = Cast<UBlueprint>(bpAsset);
      if (BlueprintAsset)
      {
        CompiledClass = BlueprintAsset->GeneratedClass;
      }
    }
    else
    {
      // Packaged Build Path: Skip UBlueprint entirely and load the compiled type direct
      // Cooked builds append "_C" to the string path of the object class definition
      FString ClassPath = bpAsset.GetObjectPathString() + TEXT("_C");
      CompiledClass = StaticLoadClass(UObject::StaticClass(), nullptr, *ClassPath);
    }

    // Now use CompiledClass to get CDO.
    // Example:
    TSubclassOf<AActor> MyClass = CompiledClass;
    AMyActor* MyActor = MyClass->GetDefaultObject<AMyActor>();


also make sure the asset is set to AlwaysCook or it may not get packaged at all

I ran into an issue with my code above. If you run in standalone mode, it won’t work. Instead of using GIsEditor, you need to do something like this:

const bool IsBP =
#if WITH_EDITOR
  true;
#else
  false;
#endif

Then use IsBP instead of GIsEditor everywhere in the original code. It should now work in editor, standalone and packaged build.

For some reason (maybe different engine version?), in my case, the string obtained from bpAsset.GetObjectPathString() already had the _C attached at the end. I removed the + TEXT("_C") part, and it worked perfectly!