[SalesForce] tell which fields are present in an sObject variable

Is there a way to retrieve the list of fields in a specific instance of an sObject?
That is, not all the fields that are defined on that sObject, but those fields which are populated on a particular instance of it in code.

Explanation of the problem

I can create an instance of account as follows

Account myAcc = new Account(firstname = 'Doug', billingCity=null);

If I do a System.debug I can see that this is an account, with values for the two fields which I have specified.

System.debug(myAcc); // This outputs Account:{BillingCity=null, FirstName=Doug}

Now if I am working with that variable myAcc, how do I know that it has values for firstname and billingCity, but not for (say) personemail?

I can retrieve the value of a given field by name. This isn't good enough as I can't tell which fields are null because they were explicitly made so (in this example billingCity) or null because they are not specified.

System.debug(myAcc.get('firstname'));    // This outputs Doug
System.debug(myAcc.get('billingcity'));  // This outputs null, it has been set to null
System.debug(myAcc.get('personemail'));  // This also outputs null, but it is not being set

This is important because if I issue an update on myAcc it updates only the explicitly set fields. So in this case it an update would set the firstname to Doug, would blank out the billingcity, but would leave the personemail untouched.

This matters to me as I am trying to merge two or more of them in code before doing an update.

Looking for a solution

Given the get syntax that you can use on an sObject, I was hoping I could get some map or list like behaviour out of it, but I haven't had any luck.

// Can we get a list of the fields that are in the instance?
System.debug(myAcc.keyset());  // Method does not exist or incorrect signature: [SOBJECT:Account].keyset()
// Can we retrieve something by index rather than name, and iterate?
System.debug(myAcc.get(0));    // Method does not exist or incorrect signature: [SOBJECT:Account].get(Integer)

I could serialize it into JSON, and then parse through it but that's pretty horrible and heavyweight.

Is there a nice easy solution to finding which fields are present in an instance of an sObject?

Best Answer

You can use JSON.deserializeUntyped() to convert any JSON-serialized SObject into a Map<String,Object> containing as its keys fields actually present on that SObject. Using your example with Account:

Account myAcc = new Account(Name = 'Doug', BillingCity=null);

// Serialize our Sobject into JSON
String s = JSON.serialize(myAcc);

// Deserialize it back into a key/value map
Map<String,Object> obj = 
    (Map<String,Object>) JSON.deserializeUntyped(s);

// Build a set containing the fields present on our SObject
Set<String> fieldsPresent = obj.keyset().clone();
// Salesforce always adds an extra 'attributes' key to SObjects
// when you do JSON stuff, so let's get rid of that.
fieldsPresent.remove('attributes');

System.debug(fieldsPresent);

// Returns: {Name,BillingCity}

There's nothing "horrible" or "heavyweight" about serializing into JSON / deserializing temporarily to get this information. Won't hurt your heap-size at all (as long as you don't maintain references after you're through with your work), and it only takes a couple of script statements.

Related Topic