'Is there a way to retrieve Salesforce picklist values for record types using apex?

I need to gather information about what picklist values are available for every record type. I know this can be achieved using either describeLayout or readMetadata API. But when I try to gather this info for a large custom object, troubles happen. The SalesForce API returns a record type with all available picklist values for it.

<recordTypeMappings>
    <name>Record1</name>
    <picklistsForRecordType>
        <picklistName>Picklist1</picklistName>
        <picklistValues>
            ...
        </picklistValues>
    </picklistsForRecordType>
    <picklistsForRecordType>
        <picklistName>Picklist2</picklistName>
        <picklistValues>
            ...
        </picklistValues>
    </picklistsForRecordType>
</recordTypeMappings>
<recordTypeMappings>
    <name>Record2</name>
    <picklistsForRecordType>
        <picklistName>Picklist1</picklistName>
        <picklistValues>
            ...
        </picklistValues>
    </picklistsForRecordType>
    <picklistsForRecordType>
        <picklistName>Picklist2</picklistName>
        <picklistValues>
            ...
        </picklistValues>
    </picklistsForRecordType>
</recordTypeMappings>

It means if I have a large object (which includes 200 picklists and 100 record types), I will get 200*100=20,000 picklist records. It makes the API response extremely large, up to 80MB. And this is extremely inefficient, if a picklist values remain the same for all record types, they will still be included in each of them in API response.

The idea is to get unique picklist values sets and then just include record ids with them, so the same picklist will not be duplicated with every record type.

<recordTypeMappings>
    <name>Record1, Record2</name>
    <picklistsForRecordType>
        <picklistName>Picklist1</picklistName>
        <picklistValues>
            ...Values which are the same for Record1 and Record2...
        </picklistValues>
    </picklistsForRecordType>
    <picklistsForRecordType>
        <picklistName>Picklist2</picklistName>
        <picklistValues>
            ...Values which are the same for Record1 and Record2...
        </picklistValues>
    </picklistsForRecordType>
</recordTypeMappings>

This will reduce the response size. Is there a way to do that in Apex? I searched in API and was not able to find anything suitable. Apex seems a better solution, since all the processing will happen on the Salesforce side.

Thanks for help.



Solution 1:[1]

To filter out duplicates and only get unique values, try capturing a collection of picklist values in a Set. For example here is a function that takes a List of fields (in this case it is a list of picklist fields) and returns a set of unique picklist values.

// Given a list of picklist fields, return a set of unique picklist values
 Set<Schema.PicklistEntry> getUniquePickListValues(List<Schema.DescribeFieldResult> pickListFields) {

     Set<Schema.PicklistEntry> uniquePicklistValues = Set<Schema.PicklistEntry>();

     for(Schema.DescribeFieldResult pickList : pickListFields){

        List<Schema.PicklistEntry> pickListValues = pickList.getDescribe().getPicklistValues();

        for(Schema.PicklistEntry entry : pickListValues){

            uniquePicklistValues.add(entry);

        }

     }

     return uniquePicklistValues;

   }

I know that using nested loops is inefficient but I don't know if there is a better way to merge list of objects into a Set.

Hope this helps.

Solution 2:[2]

If you want to retrieve picklist values based on record type, please check my solution here, https://salesforce.stackexchange.com/questions/103837/how-do-i-get-the-intersection-of-recordtype-and-picklist-values-inside-apex/202519#202519

It uses a REST API call, but the response will be similar to a getdescribe result plus record type info.

Solution 3:[3]

Here is how the performance and volume issues were solved.

The challenge was to collect available picklist values for all the record types of huge custom objects with a lot of record types and picklists. First of all, I did not find any way to do that directly in Apex, I used API calls.

When we receive a custom object description via describeSObject call (https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_calls_describesobject.htm), we get all the picklist values and record type values. What we do not get is the specific picklist values available for each record type. For that we need to execute describeLayout request (https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_calls_describelayout.htm). Using information from describeSObject we may try to predict how large describeLayout response will be.

For example, if we have 500 picklist values and 20 record types, total response of describeLayout will have up to 500*20=10,000 picklist values (since describeLayout returns all picklist values available for for each record type). Then we need to approximate how large that XML response would be, since Salesforce API has a response limit of 5mb. After inspecting the response I found out that to match the 5mb limit we need to meet the requirement of less than 30,000 picklist values per describeLayout request.

Solution was to break this large call into several smaller by record types, so we retrieve all picklist values for a few record types and then repeat for others.

It took up to 24 API requests to retrieve 70MB of data from SalesForce API, which was not possible via one API call because of response size limit.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 psun
Solution 3 Maks Shal