15

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? 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? 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. Jan 22, 2016 at 1:30

3 Answers 3

16

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());
}
3
  • 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.
    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
    Jan 22, 2016 at 10:51
  • Thank you for sharing the solution ~ Jan 11, 2021 at 1:45
4

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.
    – Shanerk
    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 .