Whilst Salesforce exposes some merging functionality in Apex, this only works for records which are persisted in the database. What about if you wish to merge two sObjects (perhaps both representing the same persisted record) during an execution?
One of my colleagues recently posed the following question:
Is there an elegant way in Apex to merge two objects records that have the same ID, but different values. Eg.
Account accOne = new Account(); accOne.Id = '001ABC123'; // same Id accOne.BillingStreet = 'Street'; accOne.BillingCity = 'City'; accOne.BillingCountry = 'Country'; Account accTwo = new Account(); accTwo.Id = '001ABC123'; // same Id accTwo.ShippingStreet = 'Street'; accTwo.ShippingCity = 'City'; accTwo.ShippingCountry = 'Country';
Technically one account record, but it exists as two in my Apex class. I want to update that one account in a single call
I know I can do:
accOne.ShippingStreet = accTwo.ShippingStreet; accOne.ShippingCity = accTwo.ShippingCity;
But is there a better way?
My first thought was that the only way this could be done was with dynamic apex – perhaps using a field set / for-loop. This however is still quite messy – so I tried to find a cleaner way to do it.
Enter JSON serialisation / deserialisation…
The JSON methods in Salesforce for serialising and deserialising are actually pretty good. The best bit is being able to supply class/object, and have the deserialiser automatically convert a string of JSON into an instance of that class. You can even automatically generate your wrapper classes with the brilliant JSON2Apex tool courtesy of @metadaddy and @superfell!
In our scenario, we want to use the JSON methods to:
- Serialize the SObjects into a JSON string
- Deserialize the JSON strings into two Maps
- Combine the Maps
- Serialize the combined Map into a JSON string
- Deserialize the (combined) JSON String into an Account
This sounds much more long winded that it is – as the serialization/deserialization can typically be done in one line.
The output looks like this:
Account a = new Account(); a.Name = 'Tom'; Account b = new Account(); b.Phone = '123'; // serialize account object to JSON string, then deserialize into a Map Map<String, Object> myMap = new Map<String, Object>(); myMap.putAll((Map<String, Object>)JSON.deserializeUntyped(JSON.serialize(a))); myMap.putAll((Map<String, Object>)JSON.deserializeUntyped(JSON.serialize(b))); // serialize map into JSON string, then deserialize into an Account Account finalAccount = (Account)JSON.deserialize(JSON.serialize(myMap), Account.class); System.debug(finalAccount.Name); System.debug(finalAccount.Phone);