In Salesforce development, serializing and deserializing data are common operations when you need to send data between the client (LWC) and server (Apex). These processes ensure that complex objects can be easily converted into a format that can be transmitted over the network (serialization) and then converted back into their original form for further processing (deserialization). In this blog post, we’ll explore how to handle serialization and deserialization in both Lightning Web Components (LWC) and Apex with various examples. Check our Serializing and Deserializing Data in LWC and Apex: A Comprehensive Guide.
What is Serialization and Deserialization?
Serialization refers to the process of converting an object into a format that can be easily transmitted. In JavaScript (LWC), this is often done using JSON.stringify(). In Apex, you can use the JSON.serialize() method to serialize objects.
Deserialization is the reverse process, where the serialized data is converted back into an object. In JavaScript, this can be done using JSON.parse(). In Apex, the method JSON.deserialize() is used for this purpose.
1. Serializing Data in Apex
Apex provides the JSON.serialize() method to convert Apex objects into JSON strings. This is useful when you need to send data to a Lightning Web Component or an external system via an API.
Example: Serializing a List of Accounts:
public class AccountController {
@AuraEnabled(cacheable=true)
public static String getAccounts() {
List<Account> accounts = [SELECT Id, Name, Industry FROM Account LIMIT 5];
// Serializing the list of accounts to JSON
String jsonResponse = JSON.serialize(accounts);
return jsonResponse;
}
}
In this example, we retrieve a list of Account records and use JSON.serialize() to convert the list into a JSON string. This JSON string is then returned to the LWC for further use.
2. Deserializing Data in Apex
In Apex, you can deserialize JSON data into an object using JSON.deserialize(). This is commonly used when receiving data from external sources or LWC components.
Example: Deserializing JSON Data into an Apex Object
public class AccountController {
@AuraEnabled
public static void saveAccounts(String jsonData) {
// Deserializing the JSON data into a list of Account objects
List<Account> accounts = (List<Account>)JSON.deserialize(jsonData, List<Account>.class);
// Saving the Accounts to the database
insert accounts ;
}
}
Here, the jsonData parameter is a JSON string passed from the LWC. We use JSON.deserialize() to convert the string back into a list of Account objects before saving them to the database.
3. Serializing Data in Lightning Web Component (LWC)
In LWC, serialization is typically handled using JSON.stringify(). This method converts a JavaScript object into a JSON string, which can then be sent to Apex.
Example: Serializing Data in LWC
import { LightningElement } from 'lwc';
import saveAccounts from '@salesforce/apex/AccountController.saveAccounts';
export default class AccountManager extends LightningElement {
accounts = [
{ Name: 'Account 1', Industry: 'Tech' },
{ Name: 'Account 2', Industry: 'Finance' }
];
handleSave() {
// Serializing the accounts data to JSON before sending to Apex
const jsonData = JSON.stringify(this.accounts);
saveAccounts({ jsonData })
.then(result => {
console.log('Accounts saved successfully');
})
.catch(error => {
console.error('Error saving accounts:', error);
});
}
}
In this example, the handleSave() method serializes the accounts array to a JSON string using JSON.stringify() before sending it to the saveAccounts Apex method.
4. Deserializing Data in LWC
When receiving data from Apex in LWC, it is automatically parsed as a JavaScript object. However, if you receive raw JSON data (e.g., from an external API), you can use JSON.parse() to deserialize it into a JavaScript object.
Example: Deserializing Data from Apex in LWC
import { LightningElement, wire } from 'lwc';
import getAccounts from '@salesforce/apex/AccountController.getAccounts';
export default class AccountList extends LightningElement {
accounts;
@wire(getAccounts)
wiredAccounts({ error, data }) {
if (data) {
// Deserializing the JSON data into a JavaScript object
this.accounts = JSON.parse(data);
} else if (error) {
console.error('Error retrieving accounts:', error);
}
}
}
Here, getAccounts returns a JSON string from Apex, which we then deserialize using JSON.parse() to convert it into a JavaScript object that can be used within the component.
Check Salesforce Integration Guide.
Scenario: Serialize Account and Associated Contacts in Apex and Deserialize in LWC
We want to fetch an Account and its related Contacts, serialize the data on the Apex side, and send it to the LWC. In LWC, we will deserialize the data and display it in a table format.
1. Apex Controller: Serialize Account and Contacts
In Apex, we will use the JSON.serialize() method to serialize both Account and its related Contacts into a single JSON object. This will make it easier to send structured data to the LWC.
public class AccountContactController {
// A custom class to hold Account and its related Contacts
public class AccountContactWrapper {
public Account account;
public List<Contact> contacts;
public AccountContactWrapper(Account account, List<Contact> contacts) {
this.account = account;
this.contacts = contacts;
}
}
@AuraEnabled(cacheable=true)
public static String getAccountWithContacts(Id accountId) {
// Query the account and its related contacts
Account account = [SELECT Id, Name, Industry FROM Account WHERE Id = :accountId LIMIT 1];
List<Contact> contacts = [SELECT Id, FirstName, LastName, Email FROM Contact WHERE AccountId = :accountId];
// Create a wrapper object that contains the account and the related contacts
AccountContactWrapper wrapper = new AccountContactWrapper(account, contacts);
// Serialize the wrapper object to JSON
String jsonResponse = JSON.serialize(wrapper);
return jsonResponse;
}
}
Explanation of Apex Code:
- We define a custom class AccountContactWrapper to wrap the Account and its related Contacts.
- The getAccountWithContacts method queries the Account and its related Contacts using the provided accountId.
- The AccountContactWrapper is created with the Account and Contacts list and then serialized into a JSON string using JSON.serialize().
2. LWC: Deserialize and Display Account and Contacts
On the LWC side, we will call the Apex method to fetch the serialized data, deserialize it into a JavaScript object, and then display the Account information and its related Contacts in a simple HTML table.
import { LightningElement, api, wire } from 'lwc';
import getAccountWithContacts from
'@salesforce/apex/AccountContactController.getAccountWithContacts';
export default class AccountContactList extends LightningElement {
@api recordId; // The Account Id passed from the parent component or page
account;
contacts = [];
error;
@wire(getAccountWithContacts, { accountId: '$recordId' })
wiredAccountContacts({ error, data }) {
if (data) {
// Deserialize the JSON data returned from Apex
const parsedData = JSON.parse(data);
// Store the account information and contacts separately
this.account = parsedData.account;
this.contacts = parsedData.contacts;
} else if (error) {
this.error = error;
console.error('Error fetching account with contacts:', error);
}
}
get accountName() {
return this.account ? this.account.Name : '';
}
get industry() {
return this.account ? this.account.Industry : '';
}
}
Explanation of LWC Code:
- We use the @wire decorator to invoke the getAccountWithContacts Apex method.
- In the wiredAccountContacts function, we check if data is available and parse the JSON response using JSON.parse().
- The account and contacts are extracted from the parsed data and stored in properties this.account and this.contacts, respectively.
- We define get methods (accountName and industry) to retrieve values from the account object for display purposes.
HTML Template: Display Account and Contacts
<template>
<lightning-card title="Account and Contacts" icon-name="standard:account">
<template if:true={account}>
<div class="slds-box slds-box_xx-small">
<h3>{accountName}</h3>
<p><strong>Industry:</strong> {industry}</p>
</div>
<!-- Display Contacts in a Table -->
<template if:true={contacts}>
<lightning-datatable
data={contacts}
columns={columns}
key-field="Id"
hide-checkbox-column="true">
</lightning-datatable>
</template>
</template>
<template if:true={error}>
<p class="slds-text-color_error">Error: {error.message}</p>
</template>
</lightning-card>
</template>
Explanation of HTML Template:
- We use a lightning-card to display the Account information, such as the account name and industry.
- If contacts are available, we use the lightning-datatable component to display them in a tabular format. The columns property defines the table columns.
- If there is an error, we display the error message.
JavaScript: Define Columns for the Data Table
import { LightningElement, api, wire } from 'lwc';
import getAccountWithContacts from '@salesforce/apex/AccountContactController.getAccountWithContacts';
export default class AccountContactList extends LightningElement {
@api recordId;
account;
contacts = [];
error;
columns = [
{ label: 'First Name', fieldName: 'FirstName' },
{ label: 'Last Name', fieldName: 'LastName' },
{ label: 'Email', fieldName: 'Email' }
];
@wire(getAccountWithContacts, { accountId: '$recordId' })
wiredAccountContacts({ error, data }) {
if (data) {
const parsedData = JSON.parse(data);
this.account = parsedData.account;
this.contacts = parsedData.contacts;
} else if (error) {
this.error = error;
}
}
get accountName() {
return this.account ? this.account.Name : '';
}
get industry() {
return this.account ? this.account.Industry : '';
}
}
Serialization in Apex: We serialized an Account and its related Contacts into a single JSON structure, which is easy to pass to the LWC.
Deserialization in LWC: We used JSON.parse() to convert the serialized data into a usable JavaScript object, which we then used to display the Account information and related Contacts.
Error Handling: Errors are captured and logged in both Apex and LWC, and appropriate error messages are shown in the LWC.
Output screen:
Conclusion
Serialization and deserialization are essential techniques in Salesforce development, especially when working with LWC and Apex. By mastering these concepts, you can effectively exchange data between the client and server while maintaining the integrity and structure of the data.
In this post, we’ve explored how to serialize and deserialize data in both Apex and LWC, using simple examples for both primitive and complex objects. Remember to always validate and optimize your data exchange process for the best performance and user experience.
This article is highly misleading as it covers many scenarios (sending and receiving SObject records, for example) that DO NOT NEED ANY EXPLICIT SERIALIZATION OR DESERIALIZATION.
Instead of accepting or returning JSON-based strings, just accept or return the actual Apex class instances themselves (these can be SObjects or custom Apex classes) and let the Data Services layer automatically handle the serialization and deserialization. The latter is something that happens anyway, so you are in fact causing double-serialization and double-deserialization, which is a complete waste of time.