4

I have a SOQL query where I don't know the FROM object, meaning it can change every time. So I dynamically populate it like so

// dynamicObject is a String.

    String queryString = 'SELECT '
                            + ' Id, Name ' +
                            + ' FROM ' + dynamicObject;
    List<sObject> sourceObjects;
// Downcasting from a generic list of sobjects to the particular type of the object.
    sourceObjects = (List<SObject>)Type.forName('List<' + dynamicObject + '>').newInstance();
    sourceObjects = Database.query(queryString);

As you can see I am downcasting from a generic sObject type to a specific one. I used this solution: Dynamically assign the dataType of a Set

However now I am trying to iterate over this list of results and the only way I can iterate is if I use the SObject type which does not recognize the Name field I queried for.

I tried doing something like this:

SObject dynamicType = (SObject)Type.forName(dynamicObject).newInstance();

for(dynamicType obj: sourceObjects) {
    //something
}

However that comes back as Invalid type: dynamicType.

Is there a way to iterate over the List where I will be able to reference the Name field that's part of the query?

4

You can simply use sObject.get() to dynamically reference the field by name.

for(sObject obj: sourceObjects) {
    doSomeWork((String)obj.get('Name'));
}

Apex doesn't have the kind of type-level dynamism you're trying to use here, and it's probably useful to make a distinction between the compile-time type that's declared for a variable, and the runtime type of an actual value you assign to it.

For example, when you do this:

sourceObjects = (List<SObject>)Type.forName('List<' + dynamicObject + '>').newInstance();
sourceObjects = Database.query(queryString);

you're not affecting the compile-time type of sourceObjects. The cast only affects the value that you're assigning to that variable. In this case, the value that you assign is immediately replaced when you perform

sourceObjects = Database.query(queryString);

in the following line, but it wouldn't affect the variable's type in any case.

Likewise, when you do

SObject dynamicType = (SObject)Type.forName(dynamicObject).newInstance();

you're getting a concrete instance of that dynamically determined type, but the variable dynamicType still has the compile-time type SObject - assigning it a value whose (runtime) type is an sObject subclass doesn't change the compile-time type of the variable.

It also doesn't give you a reference to the type itself that you can use to declare other variables; you can't actually declare a variable with a dynamically-determined type like that.

  • Likely to trip unfamiliar users that it returns Object. – Adrian Larson Aug 1 '18 at 18:13
  • What do you mean when you say that the "value immediately gets thrown away when you perform" the Database.query operation? Which value gets thrown away? Am I assigning a value before I actually perform the query? Is it an empty value then? – Arthlete Aug 1 '18 at 18:18
  • 1
    @Arthlete you make two assignments to the same variable on successive lines. The first one, prior to your query, can simply be removed because it ultimately has no effect (absent any side effects, which aren't in play here). – David Reed Aug 1 '18 at 18:20
  • In this case what would be the use of this line sourceObjects = (List<SObject>)Type.forName('List<' + dynamicObject + '>').newInstance(); if it's not actually changing the compile-time type of the sourceObjects list variable? How can it be beneficial to change the type of the value for the that list? – Arthlete Aug 1 '18 at 18:26
  • In the linked question, Derek F. is using that technique to manipulate the runtime type of the List, which he later casts and assigns to a variable with a matching compile-time type. – David Reed Aug 1 '18 at 18:29
6

You can't have a dynamically typed loop variable. You have to use the generic SObject. Note that this type supports the get method.

for (SObject record : sourceRecords)
{
    String name = (String)record.get('Name');
}
  • 2
    The SObject.get method is the one you needed to know about here. – Adrian Larson Aug 1 '18 at 18:02
  • You are an absolute beast! Let me give it a try, this is fantastic! – Arthlete Aug 1 '18 at 18:02

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.