Blog-01-05

PHOTO EMBED

Tue Jun 14 2022 15:47:02 GMT+0000 (Coordinated Universal Time)

Saved by @Justus

/**
 * Example code to show the difference in heapsize for a large payload whilst handling
 * the result in 2 different ways
 * !! This is not a production implementation, only as an illustation !!
 */
// Execute the different methods to see the difference in heap size
methodOne(getRestApiQueryResponseJson(), 'records'); // Verbose
methodTwo(getRestApiQueryResponseJson(), 'records'); // Efficient

/**
 * Method 01: very readable and verbose, great way of showing what we're doing and how much memory we can save
 * Heap size	= 670119 byte <-- Quite a bit of memory used here for just
 */
void methodOne(String jsonString, String topLevelAttribute){

	// Register our starting limits
	System.debug('[METHOD 01 - VERBOSE]');
	Integer startCpuTime = Limits.getCpuTime();
	Integer startHeapSize= Limits.getHeapSize();

	// Let's deserialize the response body untyped
	Map<String,Object> myMap = (Map<String,Object>) JSON.deserializeUntyped(jsonString);

	// We extract the object list that we want to target based on the attribute name
	Object[] objectList = (Object[]) myMap.get(topLevelAttribute);

	// We want to be able to read data from each object in the object list, so we need to cast it to an object map (Map<String,Object>>)
	List<Map<String,Object>> objectMapList = new List<Map<String,Object>>();

	// We iterate the list of objects
	for(Object obj : objectList){
		
		// We cast the object to an object map and add it to the output list
		Map<String,Object> objectMap = (Map<String,Object>) obj;
		objectMapList.add(objectMap);
	}
	
	// Call the processing logic 
	handleRecordProcessing(objectMapList);

	// Output the limits info for performance measuring
	System.debug('Heap size: ' + (Limits.getHeapSize()- startHeapSize));	
}


/**
 * Method 02: We have the heavy lifting done by the cys
 * Heap size = 19 bytes <-- Wow!!! almost nothing used now :-)
 */
void methodTwo(String jsonString, String topLevelAttribute){
	
	// Register our starting limits
	System.debug('[METHOD 02 - MEMORY EFFICIENT]');
	Integer startHeapSize= Limits.getHeapSize();

	// Call the processing logic, but give our new flashy method as an argument this time
	handleRecordProcessing(
		getObjectMapListFromTopLevelAttribute(
			(Map<String,Object>) JSON.deserializeUntyped(jsonString),
			topLevelAttribute
		)
	);

	// Output the limits info for performance measuring
	System.debug('Heap size: ' + (Limits.getHeapSize() - startHeapSize));
}


/**
 * Method that contains the logic to process the records
 * In this example it just outputs the Id and Name of the record
 */
void handleRecordProcessing(List<Map<String,Object>> objectMapsToProcess){
	for(Map<String,Object> objectMap : objectMapsToProcess){
		//System.debug('Id: ' + objectMap.get('Id') + ' - Name:' + objectMap.get('Name'));
	}
}


/**
 * Method that receives an untyped deserialized JSON response where one of the top level attributes
 * is a list of objects you needs to convert to Object maps (Map<String,Object>) in a memory efficient way
 * 
 * @param objectMap The type casted untyped JSON --> (Map<String,Object>) JSON.deserializeUntyped(jsonString);
 * @param attributeName the top level attributeName that represents a list of objects inside of the JSON
 * @return a list of object maps that are now usable in the code
 */
public static List< Map<String,Object> > getObjectMapListFromTopLevelAttribute(Map<String,Object> objectMap, String attributeName){
	
	// Create a list of Object Maps to store out output
	List<Map<String,Object>> outputList = new List<Map<String,Object>>();

	// Iterate the object list from the input map, note that we cast it in the loop
	for(Object obj : (Object[]) objectMap.get(attributeName)){
		
		// Validate that the object is actually an object and NOT a list
		// Cast the objects to Object Maps inside the add() methods parameter
		if(obj instanceof Map<String, Object>){
			outputList.add((Map<String,Object>) obj);
		}
	}

	// Return our lovely new list of objects maps that we can now use without having wasted valuable heap space
	return outputList;
}


/**
 * Execute an HTTP call the the REST API that queries 10k account records with just the name populated
 * Return the response body JSON String containing these object.
 * Note: There is no real error handling here
 */
private String httpResponseBody;
public String getRestApiQueryResponseJson(){
	if(httpResponseBody == null){
		
		// Create a default request
		HttpRequest httpRequest = new HttpRequest();
		httpRequest.setMethod('GET');
		httpRequest.setEndpoint('https://api-test-service.herokuapp.com/api/sfdc-account-query?numberOfRecords=10000&prettyPrint=0');
		
		// Send the request and retrieve the JSON body
		httpResponseBody = new Http().send(httpRequest).getBody();
	}
	return httpResponseBody;
}
content_copyCOPY