Convert wrapper class to map apex

Most of the time we need to use the existing apex method which accepts custom wrapper objects as a parameter. This post will help in sending wrapper object to apex from LWC.

There are two ways to pass the custom type or complex data to apex from LWC.

1. Sending Wrapper object to Apex

We can directly pass wrapper object to apex without any serialization and deserialization process. Let us take an example we have below wrapper class or DTO class in the apex

public class AccountWrapper { @auraenabled public string Name{get;set;} @auraenabled public integer NumberOfEmployees{get;set;} @AuraEnabled public List<Contact> Contacts { get; set; } }

This wrapper object is used in apex class to get data from LWC to insert Account and Contact objects. Use cases can be different for your project where you need to get complex data from LWC.

public class AccountService { @auraenabled public static void createAccountContact(AccountWrapper wrapper) { system.debug('wrapper:'+wrapper); if(wrapper!=null) { Account act=new Account(); act.Name=wrapper.Name; act.NumberOfEmployees=wrapper.NumberOfEmployees; insert act; if(wrapper.Contacts!=null) { for(Contact ct:wrapper.Contacts) { ct.AccountId=act.id; } insert wrapper.Contacts; } } } }

To pass this AccountWrapper wrapper object to apex we have to pass JSON data in method parameter in LWC. This is similar to the normal parameter passed to apex. Before we call any apex method we have to reference that method using import in LWC. So according to your method change it in LWC code.

import { LightningElement } from 'lwc'; import createAccountContact from '@salesforce/apex/AccountService.createAccountContact'; export default class ApexWrapperCall extends LightningElement { contacts=[]; error; handleClick(e) { var contact= { LastName:'Sahni', Email:'', Phone:'9871506648' }; this.contacts.push(contact); var pass= { Name:'Dhanik', NumberOfEmployees:2, Contacts:this.contacts }; createAccountContact({wrapper:pass}) .then(result => { console.log('Data:'+ JSON.stringify(result)); }) .catch(error => { console.log(error); this.error = error; }); } }

Here is the output of apex where complete data is passed to apex.

Convert wrapper class to map apex

2. Send serialized string to Apex and deserialize it

Passing wrapper to Apex using the first approach will work in the most scenario but if it is not working then pass wrapper data using serialized string. This serialized string will be deserialized as a wrapper object (Apex object) in the Apex class.

JSON.deserialize method will be used to deserialize string into wrapper object.

AccountWrapper wrapper=(AccountWrapper)JSON.deserialize(wrapperText,AccountWrapper.class);

Apex Code:

We expect data in this method as a string. After getting the string we are converting the string in the AccountWrapper object.

public class AccountService { public class AccountWrapper { @auraenabled public string Name{get;set;} @auraenabled public integer NumberOfEmployees{get;set;} @AuraEnabled public List<Contact> Contacts { get; set; } } @auraenabled public static void createAccountContacts(string wrapperText) { system.debug('wrapperText:'+wrapperText); AccountWrapper wrapper=(AccountWrapper)JSON.deserialize(wrapperText,AccountWrapper.class); system.debug('wrapper:'+wrapper); if(wrapper!=null) { Account act=new Account(); act.Name=wrapper.Name; act.NumberOfEmployees=wrapper.NumberOfEmployees; insert act; if(wrapper.Contacts!=null) { for(Contact ct:wrapper.Contacts) { ct.AccountId=act.id; } insert wrapper.Contacts; } } } }

LWC Code to pass wrapper object as a string:

In LWC we have to create JSON objects like account variable in the below code. After creating JSON object convert it into a string to pass the apex.

import { LightningElement } from 'lwc'; import createAccountContacts from '@salesforce/apex/AccountService.createAccountContacts'; export default class ApexWrapperCall extends LightningElement { contacts=[]; error; handleClick(e) { var contact= { LastName:'Sahni', Email:'', Phone:'9871506648' }; this.contacts.push(contact); var account= { Name:'Dhanik Sahni', NumberOfEmployees:2, Contacts:this.contacts }; createAccountContacts({wrapperText:JSON.stringify(account)}) .then(result => { console.log('Data:'+ JSON.stringify(result)); }) .catch(error => { console.log(error); this.error = error; }); } }

This will give output like the below image

Convert wrapper class to map apex

References:

Stack Exchange

Stop Serialization and Deserialization of Object In Apex

Instantly share code, notes, and snippets.

You can’t perform that action at this time.

You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.

Salesforce is a big and powerful platform, both for users and for developers. It provides a lot of pre-built tools and out-of-the-box features, but there are often challenges when something unique is needed – when custom development and complex data type manipulation is required.

This is where a Wrapper Class comes to the rescue. But what is a “Wrapper Class”, and how is it used? This guide will provide everything you need to know.

What Is a Wrapper Class?

A Wrapper Class, in simple terms, is an object created in Apex – similar to a custom object but created in code. This data lives only in code during your transaction and does not consume database storage. It’s a data structure or an abstract data type that can contain different objects or collections of objects as its members. Its instances could be different data types:

  • Primitive
  • Collections
  • sObjects

What Are the Benefits of a Wrapper Class?

A Wrapper Class provides multiple benefits including:

  1. The possibility of converting the JSON string into an object (and vice versa) with little effort.
  2. Helping to combine different types of appropriate data and organizing it efficiently.
  3. The ability to complete a variety of calculations during data organization.

Also, a Wrapper Class can be used in Lightning Web Component (LWC), AURA, and Visualforce.

Use Cases of a Wrapper Class

Now, let’s take a look at some use cases with examples of a Wrapper Class usage in LWC.

Convert JSON strings into an object

One of the key features of a Wrapper Class is the possibility to convert JSON strings to object data types very easily.

In this example, we’ve received data from another system via REST API and have to show it on the Record page without saving the data to Salesforce. This can be done easily by creating a Wrapper Class with the required data structure and using JSON.deserialize to convert it.

1. Let’s imagine we have the next JSON-type response received in Apex from another system, and we’d like to show this data on the record page.

{ "Status":"200", "Message":"Order Items are successfully created in the External System with next Data.", "OrderNumber":"00000100", "OrderItems":[ { "ExternalOrderId":"ORDER-IT-145237", "Name":"Screen size 21 inch", "Family":"Hardware", "SKU":"card-rts-21", "OrderDate":"1547250828000" }, { "ExternalOrderId":"ORDER-IT-612753", "Name":"Full HD Screen with 51 inch", "Family":"Hardware", "SKU":"card-rtp-51", "OrderDate":"1547250828000" } ] }

2. A Wrapper Class will help us replicate the same data structure in Apex. To do this, we have to create a Class with the same variables and the same structure as it is in JSON.

In this case, we will have two Wrapper Classes as our JSON contains a nested array for Order Items. We’ll have a list of a Wrapper Class OrderItem in ResponseBody Wrapper Class.

public class ResponseBody { @AuraEnabled public String Status; @AuraEnabled public String Message; @AuraEnabled public String OrderNumber; @AuraEnabled public List<OrderItem> OrderItems; } public class OrderItem { @AuraEnabled public String ExternalOrderId; @AuraEnabled public String Name; @AuraEnabled public String Family; @AuraEnabled public String SKU; @AuraEnabled public String OrderDate; }

3. After that, we can use a magic command in Apex to do the work for us. Deserialize command will transform the string to the type of object with the data type we created in a Wrapper Class.

ResponseBody responseBody = (ResponseBody)System.JSON.deserialize(json, ResponseBody.class);

4. After that, we can easily send this data to JavaScript in LWC. Using the next code, we’re calling the getOrdersList method from our Apex Controller OrderController, and to receive information about the exact order, we’re passing the Record Id of our current Order.

import { LightningElement, track, wire, api } from 'lwc'; import getOrdersList from '@salesforce/apex/OrderController.getOrdersList'; export default class OrdersList extends LightningElement { @api recordId; @track data = {}; @wire(getOrdersList, {orderId: '$recordId'}) wiredData(result) { if (result.data) { let parsedData = JSON.parse(JSON.stringify(result)); this.data = parsedData.data; } } }

5. The last thing is to show data in an appropriate way in the component. We’re using each iteration to show all records in a table.

<template if:true={data}> <h3>Order №:{data.OrderNumber}. {data.Message}</h3> <table> <tr> <th>External Id</th> <th>Product Name</th> <th>Family</th> <th>SKU</th> <th>Order Date</th> </tr> <template for:each={data.OrderItems} for:item="item"> <tr key={item.ExternalOrderId}> <td>{item.ExternalOrderId}</td> <td>{item.Name}</td> <td>{item.Family}</td> <td>{item.SKU}</td> <td> <lightning-formatted-date-time value={item.OrderDate}> </lightning-formatted-date-time> </td> </tr> </template> </table> </template>

6. Now, we’re ready to add this LWC to the lightning record page and to see the result. Before doing this, be sure to change the isExposed attribute to “true” and add your target page to properties in Lightning Web Component XML file.

<isExposed>true</isExposed> <targets> <target>lightning__RecordPage</target> </targets>

7. Here are the next steps:

  • Navigate to the Record Detail page.
  • Click on Gear icon and select Edit Page.
  • On the left side in the Custom Components section, find your orderConsolidateData component and drag it to the page.
  • Click Save and activate.

Convert wrapper class to map apex

8. As you can see, our table with data from JSON is shown on the page. Our goal has been reached with a few lines of code and some data conversions.

Combine and organize data

One more key use case is to consolidate a set of different fields from multiple Objects. For example, we need to show a list of rows that contain data from two different objects that are not related, and map them by some criteria.

Here would be a good place to use a Wrapper Class with a new data type that will consolidate fields from both objects and then iterate via this list.

1. First, we need to get records from both objects that we are going to consolidate. In our example, it’s Order Item and Product Warranty.

// Querying Order Items of Order by OrderId received from UI List<OrderItem> orderItems = [SELECT Id, OrderId, OrderItemNumber, PricebookEntry.Product2.Name, PricebookEntry.Product2Id, Product2Id, Order.BillToContactId, Quantity, UnitPrice FROM OrderItem WHERE OrderId =: orderId]; List<Product_Warranty__c> productWarranties = [SELECT Id, Name, Contact__c, Product__c, Warranty_End_Date__c, Is_Active__c, Note__c FROM Product_Warranty__c];

2. The next step will be to map Product Warranty records with a unique key that will be used in the Order Item loop to find a matching Product Warranty record.

Map<String, Product_Warranty__c> productWarrantyByKey = new Map<String, Product_Warranty__c>(); //sorting Product Warranty records by key contained from Contact and Product for(Product_Warranty__c productWarranty : productWarranties){ String productWarrantyKey = (String)productWarranty.Contact__c + (String)productWarranty.Product__c; productWarrantyByKey.put(productWarrantyKey, productWarranty); }

3. After that, we’re ready to create a Wrapper Class with all attributes from both objects.

// Wrapper class for combined data public class OrderItemProductWarrantyWrapper { @AuraEnabled public String OrderItemId; @AuraEnabled public String OrderItemName; @AuraEnabled public Boolean IsActive; @AuraEnabled public Decimal Quantity; @AuraEnabled public Double UnitPrice; @AuraEnabled public Date WarrantyEndDate; @AuraEnabled public String Note; }

4. The last step is to fill our list of Wrapper instances with combined records. In the first part, we populate variables from the record Order Item. In the second we’re finding matching Product Warranty records by Key and copying needed values to our Wrapper.

// Creating a list of OrderItemProductWarrantyWrappers for combined records List<OrderItemProductWarrantyWrapper> orderItemProdWarrantyWrappers = new List<OrderItemProductWarrantyWrapper>(); // Loop for populating our list for(OrderItem item: orderItems){ OrderItemProductWarrantyWrapper ow = new OrderItemProductWarrantyWrapper(); // Populating wrapper with item data ow.OrderItemId = item.Id; ow.OrderItemName = item.PricebookEntry.Product2.Name; ow.Quantity = item.Quantity; ow.UnitPrice = item.UnitPrice; // Populating Wrapper with consolidated data from Product Warranty by mapping with a key String oderItemKey = (String)item.Order.BillToContactId + (String)item.Product2Id; Product_Warranty__c currentProdWarranty = productWarrantyByKey.get(oderItemKey); if(currentProdWarranty != null) { ow.WarrantyEndDate = currentProdWarranty.Warranty_End_Date__c; ow.Note = currentProdWarranty.Note__c; ow.IsActive = currentProdWarranty.Is_Active__c; } orderItemProdWarrantyWrappers.add(ow); }

5. When data is structured, we’re passing it to our LWC and showing it in a table by iterating through the list as in the previous example. Then you should be able to see the result as this component.

Convert wrapper class to map apex

Summary

Using a Wrapper Class is already a best practice, but to make it even better, here is a few final recommendation on usage.

Always try to keep your Wrapper Class as a separate Apex Class, as this will help to make it unified. It also gives another developer the chance to reuse it in future implementations, avoiding code duplication. However, if you decide that your Wrapper is very unique and there is no reason to make a separate class, in the end, it’s better to keep it as Inner Class.