While writing a class to help me manage @future
jobs for a project, I stumbled onto an error message I can find very little help on.
Cannot deserialize JSON as abstract type
I used an interface named FutureHandler
, which instances extend in order to provide functionality based on their needs. The caller passes an instance of FutureHandler
to FutureManager
, who defers the call to the correct context and ensures that the Handle
method is called.
Heres some sample code:
Future Manager
public class FutureManager {
public static void ExecuteInFuture(FutureHandler handle) {
if (Utility.CanRunFuture()) {
Execute(JSON.serialize(handle), handle.Name());
} else {
Execute_sync(handle);
}
}
@future
private static void Execute(String jsonHandle, String typeName) {
FutureHandler handle = (FutureHandler)JSON.deserialize(jsonHandle, Type.forName(typeName));
// Name required in order to deserialize ('Cannot deserialize to abstract type')
Execute_sync(handle);
}
private static void Execute_sync(FutureHandler handle) {
handle.Handle();
}
}
Future Handler
public interface FutureHandler {
void Handle();
String Name();
}
Sample Implementation
public class UpdateSomeRecords_future implements FutureHandler {
List<Id> targetRecordIds { get; set; }
public CreateClonedProducts_future(List<Id> targetRecordIds) {
this.targetRecordIds = targetRecordIds;
}
public void Handle() {
update [SELECT Id FROM Account WHERE Id IN :targetRecordIds];
}
public String Name() {
return 'UpdateSomeRecords_future';
}
}
The above code works as expected- the implementation is called correctly. The message I can't seem to get around comes from a refactor attempt to remove the Name()
method from the interface.
@future
private static void Execute(String jsonHandle) {
FutureHandler handle = (FutureHandler)JSON.deserialize(jsonContainer, Type.forName('FutureHandler'));
Execute_sync(container);
}
The above snippet was an attempt to get the value as a instance of the interface, as opposed to the implementation, to avoid calling Name()
, and simply the implementations. This attempt nets me the error:
Cannot deserialize JSON as abstract type
Which makes sense - I am trying to deserialize some object as an interface. I figured, well, lets try wrapping the interface in a concrete class, to try to avoid casting directly to that type. This let me to make a FutureContainer
class, which holds a reference to the FutureHandler
.
public class FutureContainer {
FutureHandler handler { get; set; }
public FutureContainer(FutureHandler handler) {
this.handler = handler;
}
public void Handle() {
handler.Handle();
}
}
This also fails, with the same error message. I assume its due to the variable being a interface, and when the JSON class tries to decode the variable, it encounters the same error.
I'm kind of at a loss on how to deserialize an interface type without using a identify function built into the interface.
My next idea is to switch the type of FutureHandler
to a virtual class, allowing implementations to extend it, and giving a concrete target for the JSON class. I'm not a fan of this method, as I don't have a default behavior to modify, which feels against the spirit of using a virtual class.
What are other methods I can use to deserialize abstract objects?