You can use a Data Asset to spawn a new Actor in the editor viewport by creating a UActorFactory. Once created, drag-drop your data asset into the viewport to spawn the actor.

Example

//.h
UCLASS()
class MYGAME_API UKotItemDefinitionActorFactory : public UActorFactory
{
	GENERATED_BODY()
 
public:
	virtual bool CanCreateActorFrom(const FAssetData& AssetData, FText& OutErrorMsg) override;
	virtual UClass* GetDefaultActorClass(const FAssetData& AssetData) override;
	virtual bool CanPlaceElementsFromAssetData(const FAssetData& InAssetData) override;
};
 
//.cpp
bool UKotItemDefinitionActorFactory::CanCreateActorFrom(const FAssetData& AssetData, FText& OutErrorMsg)
{
	if(!AssetData.IsValid() || !AssetData.IsInstanceOf(UKotItemDefinition::StaticClass()))
	{
		OutErrorMsg = INVTEXT("a valid Item Definition must be defined");
		return false;
	}
 
	return true;
}
 
UClass* UKotItemDefinitionActorFactory::GetDefaultActorClass(const FAssetData& AssetData)
{
	if(auto* ItemDef = Cast<UKotItemDefinition>(AssetData.GetAsset()))
	{
		return ItemDef->GetItemClass().LoadSynchronous();
	}
 
	return nullptr;
}
 
bool UKotItemDefinitionActorFactory::CanPlaceElementsFromAssetData(const FAssetData& InAssetData)
{
	return InAssetData.IsValid() && InAssetData.IsInstanceOf(UKotItemDefinition::StaticClass());
}

Possible gotchas/issues

It seems in some cases the UPlacementSubsystem which is used to determine what actor factories are available can run earlier than the module which contains the actor factory.

The result is the data asset failing to drag-drop into the viewport.

You can fix this by manually registering your actor factory with the Placement Subsystem:

//Place this code into your module's startup:
void FMyEditorModule::StartupModule()
{
	if(GUnrealEd)
	{
		GUnrealEd->GetEditorSubsystem<UPlacementSubsystem>()->RegisterAssetFactory(NewObject<UKotItemDefinitionActorFactory>());
	}
}