16

Following code is giving me common.apex.runtime.impl.ExecutionException

String jsonText = '{"Data": {"attributes": {"type": "Contact"},"Id": "0036100000JUXKkAAP","Description": "","LastName": "Testing"}}';
Map<String, Object> cObjMap = (Map<String, Object>) JSON.deserializeUntyped(jsonText); String cObjJson = JSON.serialize(cObjMap.get('Data')); SObject customObject = (SObject)JSON.deserialize(cObjJson, SObject.class);

The problem is in fact that Map<String,Object> gets re-ordered during serialize/deserialize and "attributes" node is moved to last position.

That causes deserialize to SObject to fail as it requires "attributes" to be in first position, just like in jsonText example.

I know that defining separate class that would map all key/value pairs from JSON and deserializing to it would work, but I don't have exact structure as keys might vary.

Do you have any idea how to accomplish this?

Thank You!

3
  • I'm pretty sure that the order won't get switched - maps are deterministic now. Can you confirm that "attributes" ends up at the end? Commented Jan 21, 2016 at 23:13
  • 2
    Hmm, it does seem to mix it up, actually - going into the map. However, execute anon has no problem with this. Where are you seeing the error? Commented Jan 21, 2016 at 23:37
  • What type is cObjMap.get('Data')? If you try and cast it to sObject you get the error: Invalid conversion from runtime type Map<String,ANY> to SObject. So at best it is Map<String, Object>. Yes you can serialize it, but I don't think you can deserialize it as an sObject class instance. Nor can you deserialize a Map<String, Object> as Object isn't supported. Commented Jan 22, 2016 at 1:30

3 Answers 3

17

Well, this is how you can do it. Apex is made through Java!

String jsonText = '{"Data": {"attributes": {"type": "Contact","url":"/services/data/v35.0/sobjects/Contact/0036100000JUXKkAAP"},"Id": "0036100000JUXKkAAP","Description": "","LastName": "Testing"}}';

Map<String, Object> cObjMap = (Map<String, Object>) JSON.deserializeUntyped(jsonText);
String cObjJson = JSON.serialize(cObjMap.get('Data'));
// Why again :(
Map<String, Object> cObjMapFurious = (Map<String, Object>) JSON.deserializeUntyped(cObjJson);
String cObjJsonDrunk = JSON.serialize(cObjMapFurious);
try
{
    SObject customObject = (SObject)JSON.deserialize(cObjJsonDrunk, Sobject.class);
    System.debug(' Accomplished: '+customObject);
}
catch(Exception ex)
{
    System.debug(' @@@@@ Don\'t visible '+ex.getMessage());
}
2
  • Thanks Ashwani, it works! Anyway, this looks more like a workaround for something that should be supported initially. I assume that your logic was to deserialize/serialize once again so the key-value pairs gets reverted back to starting state, with 'attributes' at first position.
    – Nikola J.
    Commented Jan 22, 2016 at 10:30
  • @NikolaJ. We are doing this for years! Thats the object serialization algorithm behavior used by Salesforce Apex :) They day it will be modified , no impact would occur in this code.
    – Ashwani
    Commented Jan 22, 2016 at 10:51
5

Things must have changed signficantly in the past few years, because it's quite simple now from what I can see as of Winter '20. It's a 1 line task to deserialize. In this example, I'm deserializing an external object to use in a Unit Test Mock.

Part1 -- Serialize to a String.

User__x testExtUser = [select Id, ExternalId, Name__c, FederationIdentifier__c, IsActive__c from User__x limit 1];
String jsonString = Json.serialize(testExtUser);

Part 2 Deserialize - the example shows the actual Json contents as a string literal.

User__x fscTestUser = (User__x) JSON.deserialize('{"attributes":{"type":"User__x","url":"/services/data/v48.0/sobjects/User__x/0051I000002KgLPQA0"},"Id":"x0J1I000002KgLPUA0","Name__c":"John Smith","IsActive__c":true,"FederationIdentifier__c":"smi970","ExternalId":"0051I000002KgLPQA0"}', SObject.class);
1

I think this way is easier

Map<String, Object> d = new Map<String, Object>{
  'CreatedDate' => Datetime.now()
};
Lead l = (Lead)JSON.deserialize(JSON.serialize(d), Type.forName('Lead'));
System.debug(l);
1
  • If you want to do anything with reflection instead of hard coding all your type references, Type.forName() is clutch because for some dumb reason you cannot cast Schema.SobjectType to System.Type. Commented Aug 10, 2022 at 20:18

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .