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?

2 Answers 2

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.

5
  • Likely to trip unfamiliar users that it returns Object.
    – Adrian Larson
    Aug 1, 2018 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, 2018 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, 2018 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, 2018 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, 2018 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
  • 2
    The SObject.get method is the one you needed to know about here.
    – Adrian Larson
    Aug 1, 2018 at 18:02
  • You are an absolute beast! Let me give it a try, this is fantastic!
    – Arthlete
    Aug 1, 2018 at 18:02

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .