7

My code is

 myOutput = SELECT location__c, level__c, group__c, count__c from resource__c
 public sobject[] data = Database.query(myOutput);

myOutput is an user input value, based on this query I have to list the fields to a selectList. Here in this case, I have to show the list of fields location_c, level_c, group_c, count_c. Please help me to do this in apex.

The below code will display all the fields from the object resource__c, but I want only the fields in the query entered by the user.

Map<String, Schema.SObjectField> schemaFieldMap = Schema.SObjectType.resource__c.fields.getMap();
    for (String fieldName: schemaFieldMap.keySet()) {
        options.add(new SelectOption(fieldName, fieldName));  
    } 
  • I couldn't understand what do you mean by "I want only the fields in the query entered by the user." – Ganesh Bhosle Jan 16 '14 at 5:47
  • myOutput is an input field where I am getting value (Query) from the user, here in this example user entered the query "SELECT location__c, level__c, group__c, count__c from resource__c". My requirement is to list the fields in this query to an selectList. – staticraj Jan 16 '14 at 5:54
  • better parse the input query(myOutput ) – Ganesh Bhosle Jan 16 '14 at 5:57
14

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 = user@domain.com

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={
        Username=andy-generaldev@financialforce.com,
        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
    }
}
  • Now, I'm using the below code to get the fields String[] jsonFields = new List<String>(); for (String qFields: queriedFieldValues.keySet()) { if (! (qFields.equalsIgnoreCase('Id') || qFields.equalsIgnoreCase('attributes'))){ jsonFields.add(qFields); } } but for the below query, I'm getting values as manager__c, resource__r & resource__c SELECT manager__c, resource__r.group__c, resource__r.location__c FROM management__c ORDER BY resource__r.location__c Your help is appreciated. – staticraj Jan 17 '14 at 9:40
  • Yes sure when I get a chance later today I'll update to show how to reference related fields via the maps returned by the JSON calls. – Andrew Fawcett Jan 17 '14 at 15:29
  • Ok I've updated my answer with a revised and improved approach, this should do what you need! :-) – Andrew Fawcett Jan 18 '14 at 2:36
  • Thanks for your detailed explanation and solution. I'm struck in the below query, JSON created array of objects for field - records. Query: SELECT group__c, (SELECT manager__c FROM managements__r) FROM resource__c USER_DEBUG|[77]|DEBUG|{Group__c=Airline & Platforms, Id=a07i00000087ZJjAAM, Managements__r={done=true, records=({Id=a09i0000005tAaOAAU, Manager__c=Joseph, Resource__c=a07i00000087ZJjAAM, attributes={type=Management__c, url=/a09i0000005tAaOAAU}}), totalSize=1}, attributes={type=Resource__c, url=//sobjects/Resource__c/a07i00000087ZJjAAM}} Any Solution? – staticraj Jan 20 '14 at 13:05
  • Ah a subselect, we could be going quite a while adapting and improving this code i feel. The general idea though is your parsing in a generic way the JSON serialised form. Take the output from JSON.serialize and run it through JSONLint web site to make it readable, the adapt the code I've given you. You may have to figure some other data structure in memory to reflect subselects and again be recursive as the subselect query can use many of the features of the outer query. If your still struggling might have to move this to another question... – Andrew Fawcett Jan 20 '14 at 23:09
5

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()), ',');

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.