Locating Salesforce Compound and Component Fields in Apex and Python
- David Reed
One of the odder corners of the Salesforce data model is the compound fields. Coming in three main varieties (Name fields, Address fields, and Geolocation fields), these fields are accessible both under their own API names and in the forms of their component fields, which have their own API names. The compound field itself is always read-only, but the components may be writeable.
For example, on the
Contact object is a compound address field
OtherAddress. (There are a total of four standard Address fields spread across the
Account objects, with a handful of others across
Order, and so on). The components of
Contact has a compound
Name field, as do Person Accounts, with components like
So, if we're working in dynamic Apex or building an API client, how do we acquire and understand the relationships between these compound and component fields?
In the REST API, the Describe resource for the sObject returns metadata for the object's fields as well. This makes it easy to acquire all the data we need in one go.
yields, on a lightly customized Developer Edition, about 250KB of JSON. Included is a list under the key
"fields", which contains the data we need (abbreviated here to omit irrelevant data points):
Each field includes its API name (
"name"), its label, other metadata, and
"compoundFieldName". The value of this last key is either
null, meaning that the field we're looking at is not a component field, or the API name of the parent compound field. There's no marker indicating that a field is compound.
This structure can be processed easily enough in Python or other API client languages to yield compound/component mappings. Given some JSON
response (parsed with
json.loads()), we can do
Likewise, we can get the components of any given field:
Both operations can be expressed in various ways, including uses of
filter(), or can be implemented at a higher level if the describe response is processed into a structure, such as a
dict keyed on field API name.
The situation in Apex is rather different because of the way Describe information is returned to us. Rather than a single, large blob of information covering an sObject and all of its fields, we get back individual describes for an sObject (
Schema.DescribeSobjectResult) and each field (
Schema.DescribeFieldResult). (We can, of course, call out to the REST Describe API in Apex, but this requires additional work and an API-enabled Session Id).
Schema.DescribeFieldResult does not include the critical
... or rather, it isn't documented to include it. In point of fact, it does contain the same data returned for a field in the API Describe call, as we can discover by inspecting the JSON result of serializing a
Unlike some JSON-enabled Apex magic, we can get to this hidden value without actually using serialization. Even though it's undocumented, these references compile and execute as expected:
This makes it possible to construct Apex utilities like we did in Python to source compound fields and compound field components. In Apex, we'll necessarily be a bit more verbose than Python, and performance is a concern in broad-based searches. Both finding compound fields on one sObject and locating component fields for one compound field take between 0.07 and 0.1 second in unscientific testing. Your performance may vary.
14:15:14:523 USER_DEBUG |DEBUG|(OtherStreet, OtherCity, OtherState, OtherPostalCode, OtherCountry, OtherStateCode, OtherCountryCode, OtherLatitude, OtherLongitude, OtherGeocodeAccuracy)
22:15:30:089 USER_DEBUG |DEBUG|(Name, OtherAddress, MailingAddress)
Simple modifications could support the use of API names rather than
SobjectField tokens, building maps between compound field and components, and similar workflows.
This post developed out of a Salesforce Stack Exchange answer I wrote, along with work on a soon-to-be-released data loader project.