How do I get the list of fields of sObject

  • 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."

    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.

    better parse the input query(myOutput )

  • 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
        }
    }
    

    Now, I'm using the below code to get the fields `String[] jsonFields = new List(); 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.

    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.

    Ok I've updated my answer with a revised and improved approach, this should do what you need! :-)

    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?

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

    Hi Andrew. Great solution. In the dumpFields() method, I replaced the break; by a continue; statement as I found out that the attributes tag is not always the last one in the keySet().

    1 more.. In order to support multilevel dynamic queries I slightly adapted the dumpFields() method by replacing dumpFields(queriedFieldName + '.', (Map) queriedFieldValue); by dumpFields(relatedField+queriedFieldName + '.', (Map) queriedFieldValue);

License under CC-BY-SA with attribution


Content dated before 7/24/2021 11:53 AM