[SalesForce] Accessing child records returned from SOQL via generic SObject

I'm currently querying the records of a custom object, including some related child records. Unfortunately the parent object is unknown, so the returned results from the query is a list of SObjects.
Is it possible to either dynamically type caste the returned results, or access the child records via an SObject?

i.e.
The following code returns a list of Accounts with child Contacts included.

String query = 'SELECT Id, Name, (SELECT Id, Name FROM Contacts) FROM Account LIMIT 2';

List<Account> listOfObjects = Database.query(query);

for(Account singleObject : listOfObjects) {
    system.debug(singleObject);
    for(Contact childContact : singleObject.Contacts) {
        system.debug('Contact Name: ' + childContact.Name);
    }
}

If you run this in execute anonymous it works as expected.
If however I change it from an Account to a generic SObject I get the error Field expression not allowed for generic SObject on the for(Contact childContact : singleObject.Contacts) line.

i.e.

String query = 'SELECT Id, Name, (SELECT Id, Name FROM Contacts) FROM Account LIMIT 2';

// Change from Account to SObject
List<SObject> listOfObjects = Database.query(query);

for(SObject singleObject : listOfObjects) {
    system.debug(singleObject);
    for(Contact childContact : singleObject.Contacts) {
        system.debug('Contact Name: ' + childContact.Name);
    }
}

Really what I'm trying to achieve is to process the child records with a single SOQL statement, but I don't think I can do this.

I can't caste with a concrete object because I don't know what type of object to caste it to until runtime.

I'm thinking I either need to:

  1. somehow dynamically caste the generic SObject to a concrete object
  2. find a way to access the child records on a generic SObject.
  3. Do a separate query on the child table
  4. have a whole bunch of if statements so that I can caste directly to the types of objects this may be relevant for

Has anyone come across this before and has a solution?

NOTE: in my proper code the objects used are custom object. The examples provided here are so that it can work for anyone that tries it.

Best Answer

There are sufficient methods on the SObject base type to write code that isn't tied at all to specific SObject types. You can get and set primitive field values and navigate across both parent and child relationships. (The only thing I have noticed missing from the API is the ability to add errors to specific SObject fields; there is only the ability to add an error to the overall SObject via SObject.addError.)

In this example you could use this code:

String query = 'SELECT Id, Name, (SELECT Id, Name FROM Contacts) FROM Account LIMIT 2';
List<SObject> parents = Database.query(query);
for(SObject parent : parents) {
    for(SObject child : parent.getSObjects('Contacts')) {
        system.debug('Child=' + child);
    }
}

The describe calls also allow you to discover field names such as the "Contacts" that I have hard coded if you want to write completely metadata driven code. Also note that when you have an ID value you can get the SObject type using Id.getSObjectType.