2

Good morning/afternoon/evening,

I'm working with some code I got from GitHub to generate a CSV from a List<Sobject> someQuery

However, it doesn't do well with parent records (i.e. Donor__r.FirstName)

I need to be able to loop through the values in a JSON object that has parent records in at as well (Contact --< Many Opps). My query is simple, its on the Opportunity and needs to pull in the values dynamically.

Here is the Method:

 private static String createSObjectString(List<String> csvFieldAPINames, List<sObject> csvRowList, String concantenatedStr){
        Integer i = 0;
        Integer rowListSize = csvRowList.size();
        Integer headerSize = csvFieldAPINames.size();
        for(sObject row :csvRowList){
            //assuming the rowList is in the correct format and each string in the list is a new record
            Map<String, Object> objFieldMap = (Map<String, Object>)JSON.deserializeUntyped(JSON.serialize(row));
            System.debug(objFieldMap);
            Integer j = 0;
            for(String k : objFieldMap.keySet()){
                System.debug(k);
                System.debug(objFieldMap.get(k));


            }
            for(String objField :csvFieldAPINames){
                concantenatedStr += objFieldMap.get(objField) != null ? String.valueOf(objFieldMap.get(objField)) : '';
                j++;
                concantenatedStr += j != headerSize ? ',' : '';//add comma separator
            }
            i++;
            if(i != rowListSize) concantenatedStr += '\n';//add new line

        }
        return concantenatedStr;
    }

Here is a JSON structure of the Map<String, Object> that is deserialized.

{
  Amount=125.00,
  Donor__c=asldkjas0912801,
  Donor__r= {// THese are values I'm unsure how to access, while still maintaining the dynamic nature of the method. 
    FirstName=John,
    Id=120957839204752ba,
    LastName=Doe,
  attributes={
    type=Contact,
    url=/services/data/v56.0/sobjects/Contact/120957839204752ba
  }
},
  Id=00917298734189hjak,
  Name=John Doe $125.00 Single Donation 11/04/2021,
  attributes={
    type=Opportunity, url=/services/data/v56.0/sobjects/Opportunity/00917298734189hjak
  }
}
2
  • 1
    How did it go? Working? Feb 1, 2023 at 5:56
  • Yes, see comment below marking yours as best answer.
    – thinker
    Feb 1, 2023 at 20:11

2 Answers 2

1

I think the best way to do this is to flatten your result into a single map, with no nesting. ie this:

{
  "Amount":   125.00,
  "Donor__c": "asldkjas0912801",
  "Donor__r.FirstName" : "John",
  "Donor__r.Id":         "120957839204752ba",
  "Donor__r.LastName":   "Doe",
  "Id":        "00917298734189hjak",
  "Name":      "John Doe $125.00 Single Donation 11/04/2021"
}

To do this, just preprocess the map using a method like this:

Map<String,Object> flattenMap(Map<String,Object> objMap){
  
  Map<String,Object> flatMap = new Map<String,Object>();
  
  for (String key : objMap.keySet()){
    //you chould check if the objMap.get(key) is an object also
    if (key.contains('__r')){
      Map<String,Object> innerMap = new Map<String,Object>();
      innerMap = (Map<String,Object>)objMap.get(key);
      for (String innerKey : innerMap.keySet()){
        String combinedKey = key + '.' + innerKey;
        flatMap.put(combinedKey,innerMap.get(innerKey));
      }
    }
    else {
      flatMap.put(key,objMap.get(key));
    }
  }
}

This way you can run your usual code and it'll all work.

The advantage of this is you need NO special checks on the object type. Just call this piece of code above like so:

objFieldMap = flattenMap((Map<String, Object>)JSON.deserializeUntyped(JSON.serialize(row)));
2
  • This worked really well. but you need to Cast to the InnerMap like this Map<String, Object> innerMap = new Map<String, Object>(); innerMap = (Map<String, Object>) objMap.get(key);
    – thinker
    Feb 1, 2023 at 20:11
  • 1
    Updated - yes this was untested. Glad it worked for you :) Feb 1, 2023 at 22:14
1

objFieldMap.get('Donor__r') would be an instanceOf sObject... so you can test for this and break down appropriately by converting to an object or converting to a map...

if ( objFieldMap.get(key) instanceOf sObject ) {
    sObject subObj = (sObject)objFieldMap.get(key);
    Map<String,Object> subObjFieldMap = subObj.getPopulatedFieldsAsMap();
}

or if you know specifically that "Donor__r" will always be of custom object Donor__c then you can cast it to a Donor record...

Donor__c objDonor = (Donor__c)objFieldMap.get('Donor__r');
System.debug(objDonor.FirstName);
2
  • Damn, I was so close! Trying now.
    – thinker
    Jan 31, 2023 at 17:50
  • Instance of didn't register the object.
    – thinker
    Feb 1, 2023 at 20:12

You must log in to answer this question.

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