How do I get the list of fields of sObject
UPDATED: Following comment request see below for related fields
There is no method on the SObject class that can be used to determine if a field has been queried. I would add this to the 'hasError' method request as well btw!
In the meantime I can see two options...
Catching SObjectExceptions
You can catch the exception that is generated from attempting to reference a field that has not been queried, thus its possible to compile a list of queried fields by catching the exception. The following example shows the idea for immediate fields on the Account record, it can be extended for relationships also, by adapting it to reference the child relationships returned by Apex Describe.
Account account = [select Id, AnnualRevenue, Website from Account limit 1];
Map<String, Schema.SObjectField> schemaFieldMap = Schema.SObjectType.Account.fields.getMap();
Map<String, Object> queriedFieldValues = new Map<String, Object>();
for (String fieldName: schemaFieldMap.keySet()) {
try {
queriedFieldValues.put(fieldName, account.get(fieldName));
} catch (SObjectException e) {
// Intentional capture
}
}
System.debug(queriedFieldValues);
The following outputs...
06:40:47.071 (71864669)|USER_DEBUG|[11]|DEBUG|{annualrevenue=30000000, id=001G000000qdARPIA2, website=www.genepoint.com}
Using JSON Serialisation
The JSON serialisation support only emits populated values, you can leverage this by serialising then immediatly deserialising the JSON. Using the untyped JSON deserialize method you can get a map of field values.
Account account = [select Id, AnnualRevenue, Website from Account limit 1];
Map<String, Object> queriedFieldValues = (Map<String, Object>) JSON.deserializeUntyped(JSON.serialize(account));
System.debug(queriedFieldValues);
This outputs the following...
07:03:46.072 (72023287)|USER_DEBUG|[3]|DEBUG|{AnnualRevenue=30000000, Id=001G000000qdARPIA2, Website=www.genepoint.com, attributes={type=Account, url=/services/data/v29.0/sobjects/Account/001G000000qdARPIA2}}
Summary
Neither option is the direct approach we would like, but on balance I'd say perhaps the second might be more efficient, though i don't know for sure, raising exceptions can be quite expensive in languages like Java as a stacktrace and other debug info needs to be resovled. So if you scale the former solution to more records, children etc it will likely not win compared to the JSON approach. Though feel free to make your own choice and tests.
Update to Show Supported for Querying Related Fields.
Further to the query in the comments regarding accessing related field information, I have revised the example code as follows to included the related fields queried.
public with sharing class QueryQueriedFields
{
public static void demo()
{
Account account = [select Id, AnnualRevenue, Website, Owner.Username from Account limit 1];
Map<String, Object> queriedFieldValues = (Map<String, Object>) JSON.deserializeUntyped(JSON.serialize(account));
dumpFields('', queriedFieldValues);
}
public static void dumpFields(String relatedField, Map<String, Object> queriedFieldValues)
{
for(String queriedFieldName : queriedFieldValues.keySet())
{
// Skip this information, its not a field
if(queriedFieldName.equals('attributes'))
break;
// Check if the queried value represents a related field reference?
Object queriedFieldValue = queriedFieldValues.get(queriedFieldName);
if(queriedFieldValue instanceof Map<String,Object>)
dumpFields(queriedFieldName + '.', (Map<String, Object>) queriedFieldValue);
else
System.debug(relatedField + queriedFieldName + ' = ' + queriedFieldValue);
}
}
}
This now outputs...
02:31:39.104 (104629544)|USER_DEBUG|[22]|DEBUG|Owner.Username = [email protected]
02:31:39.104 (104734352)|USER_DEBUG|[22]|DEBUG|Website = www.genepoint.com
02:31:39.104 (104807836)|USER_DEBUG|[22]|DEBUG|Id = 001G000000qdARPIA2
In order to understand how this works, you need to see the JSON structure, as you can see it is structured with the related fields in a substructure. In order to support related references beyond the first level, a recursive approach was taken.
{
AnnualRevenue=30000000,
Id=001G000000qdARPIA2,
Owner={
[email protected],
attributes={
type=User,
url=/services/data/v29.0/sobjects/User/005G0000001lKz1IAE
}
},
Website=www.genepoint.com,
attributes={
type=Account,
url=/services/data/v29.0/sobjects/Account/001G000000qdARPIA2
}
}
One liners:
Sobject__c.sObjectType.getDescribe().fields.getMap().keySet()
Or for dynamic queries:
String.join(new List<String>(SObject__c.sObjectType.getDescribe().fields.getMap().keySet()), ',');