Introduction
Handling asynchronous processing in Salesforce often requires using @future or Queueable methods. These features are powerful. However, they come with certain limitations. One limitation is passing complex objects like wrapper classes as input parameters.
In this blog, we’ll explore why these limitations exist and how you can work around them. We’ll also provide detailed code examples, real-world use cases, and tips for best practices.
By the end, you’ll understand:
- The restrictions of passing wrapper objects directly to future and queueable methods.
- How JSON serialization and deserialization can help.
- Practical scenarios where this approach is indispensable.
What is a Wrapper Class?
A wrapper class in Salesforce is a custom class used to encapsulate related pieces of data into a single object. This approach is commonly used to group sObjects, flags, or other data types together for easier handling.
Example Wrapper Class:
public class AccountWrapper {
public Account account { get; set; }
public Boolean isHighValue { get; set; }
public String ownerName { get; set; }
// Constructor
public AccountWrapper(Account account, Boolean isHighValue, String ownerName) {
this.account = account;
this.isHighValue = isHighValue;
this.ownerName = ownerName;
}
}
Why Can’t We Pass Wrapper Classes Directly?
Salesforce’s asynchronous methods, such as @future and Queueable, do not support passing non-primitive data types as parameters. This includes wrapper classes and custom objects.
Limitations of Asynchronous Methods:
- Future Methods:
- Only primitive data types (e.g.,
String,Integer,Id) and collections of these are allowed as parameters. - Wrapper classes are custom Apex classes. They are not primitive data types. Therefore, they cannot be passed directly to a Future method.
- sObjects or complex objects are not allowed.
- Only primitive data types (e.g.,
- Queueable Classes:
- Parameters can be more flexible, but the objects must be serializable.
- Passing unserializable data can lead to runtime errors.
The JSON Workaround
The object must first be serialized into a JSON string. This is necessary to pass a wrapper class to a future or queueable method. Once inside the asynchronous method, the JSON string can be deserialized back into the wrapper class.
How JSON Serialization Helps:
- Converts the wrapper object into a format (
String) compatible with asynchronous method parameters. - Allows data integrity and structure to remain intact.
Example: Passing a Wrapper Class to a Future Method
Step 1: Define the Wrapper Class
public class AccountWrapper {
public Account account { get; set; }
public Boolean isHighValue { get; set; }
public String ownerName { get; set; }
public AccountWrapper(Account account, Boolean isHighValue, String ownerName) {
this.account = account;
this.isHighValue = isHighValue;
this.ownerName = ownerName;
}
}
Step 2: Create a Future Method
public class AsyncProcessor {
@future
public static void processWrapper(String wrapperJson) {
// Deserialize the JSON back into the wrapper class
AccountWrapper wrapper = (AccountWrapper) JSON.deserialize(wrapperJson, AccountWrapper.class);
// Perform actions using the wrapper data
if (wrapper.isHighValue) {
wrapper.account.Description = 'High-value customer';
update wrapper.account;
}
}
}
Step 3: Call the Future Method
public static void triggerFuture() {
Account acc = [SELECT Id, Name FROM Account LIMIT 1];
AccountWrapper wrapper = new AccountWrapper(acc, true, 'John Doe');
// Serialize the wrapper object to JSON
String wrapperJson = JSON.serialize(wrapper);
// Call the future method
AsyncProcessor.processWrapper(wrapperJson);
}
Example: Passing a Wrapper Class to a Queueable Class
Step 1: Define the Wrapper Class
public class AccountWrapper {
public Account account { get; set; }
public Boolean isHighValue { get; set; }
public String ownerName { get; set; }
public AccountWrapper(Account account, Boolean isHighValue, String ownerName) {
this.account = account;
this.isHighValue = isHighValue;
this.ownerName = ownerName;
}
}
Step 2: Create a Queueable Class
public class QueueableProcessor implements Queueable {
private String wrapperJson;
// Constructor to pass JSON
public QueueableProcessor(String wrapperJson) {
this.wrapperJson = wrapperJson;
}
public void execute(QueueableContext context) {
// Deserialize the JSON back into the wrapper class
AccountWrapper wrapper = (AccountWrapper) JSON.deserialize(wrapperJson, AccountWrapper.class);
// Perform actions
if (wrapper.isHighValue) {
wrapper.account.Description = 'Processed via Queueable';
update wrapper.account;
}
}
}
Step 3: Enqueue the Job
public static void triggerQueueable() {
Account acc = [SELECT Id, Name FROM Account LIMIT 1];
AccountWrapper wrapper = new AccountWrapper(acc, true, 'John Doe');
// Serialize the wrapper object to JSON
String wrapperJson = JSON.serialize(wrapper);
// Enqueue the Queueable job
System.enqueueJob(new QueueableProcessor(wrapperJson));
}
Real-World Use Cases
1. Bulk Processing Accounts
Imagine a scenario where you need to process a list of high-value accounts asynchronously. The wrapper class can hold additional metadata (e.g., flags and user inputs) required for processing.
2. Integration with External APIs
Wrapper classes are useful for grouping API request/response data. When integrating Salesforce with third-party systems, using JSON makes data transfer seamless.
3. Asynchronous Data Transformation
Queueable classes can leverage deserialized wrappers to apply complex transformations on data before further processing or database updates.
Best Practices
1. Error Handling
Always include try-catch blocks in your asynchronous methods to handle JSON parsing errors gracefully.
try {
AccountWrapper wrapper = (AccountWrapper) JSON.deserialize(wrapperJson, AccountWrapper.class);
} catch (Exception ex) {
System.debug('Error during JSON deserialization: ' + ex.getMessage());
}
2. Limit JSON Payload Size
Ensure the JSON payload size does not exceed Salesforce limits. Optimize wrapper design to include only essential fields.
3. Testing
- Use test data factories to create sample wrapper objects.
- Assert that JSON serialization/deserialization works as expected.
- Cover positive and negative scenarios for your future and queueable methods.
4. Document the Wrapper Structure
Add comments to the wrapper class. This will make it easier for other developers to understand its purpose and structure.
Conclusion
JSON serialization and deserialization offer a powerful way to overcome limitations. These limitations are involved in passing wrapper classes directly to future and queueable methods in Salesforce. By adopting this approach, developers can ensure flexibility, maintainability, and scalability in their asynchronous processing.
Try these techniques in your next project and see the difference in how efficiently you handle complex asynchronous tasks!


Leave a comment