2

How can the following list of records be serialized/deserialized into a map or collection?

Account[] accs = [select id, name, custom_parent__r.name, (select lastname, firstname from Contacts) from Account limit 2];

Essentially I want to access child/parent records dynamically without having any upfront knowledge about them.

Btw this is a more complete (and complicated) version of this SSE question.

0

JSON is dead simple in Apex Code, and here's how.

First, you need some helper classes (they can be inner classes inside the class that holds your function):

public class ParentRecord {
    public String recordType, entity, job;
    public ChildRecord[] item;
    public ParentRecord(String recordType, String entity, String job) {
        this.recordType = recordType;
        this.entity = entity;
        this.job = job;
        item = new ChildRecord[0];
    }
}

public class ChildRecord {
    public String item;
    public Decimal quantity, amount;
    public ChildRecord(String item, Decimal quantity, Decimal amount) {
        this.item = item;
        this.quantity = quantity;
        this.amount = amount;
    }
}

Then, you build your Data:

// Query parent and children, you can do both at once.
// Replace object names, relationship names, and field names.
Parent record = [SELECT Id, recordType, entity, job, (SELECT Id, Item, quantity, amount FROM Children) FROM Parent WHERE Id = :someId];

// Create a parent record entry from the utility class
ParentRecord parent = new ParentRecord(record.recordType, record.Entity, record.job);
// Loop through and build the item list
for(Child lineItem: record.Children) {
    parent.item.add(new ChildRecord(lineItem.Item, lineItem.quantity, lineItem.Amount));
}

Now, at this point, you have just one task left: Converting the data to JSON. This is where the utility classes come in handy. Since all the data is now neatly laid out, we simply do this:

String jsonPayload = JSON.serialize(parent);

This takes care of all the little details for you. Quotes are escaped, numbers are properly formatted, nulls are defined correctly, and braces, brackets, commas, and colons all find their way to the right place with no effort on your part.

Finally,use the data whenever you want to use.

  • Your script still requires me to know about child and lookup relationships. As the question states I have no prior knowledge of them. I don't want the caller to do any work (i.e. in your example records are converted into wrappers before being sent for dynamic processing). – Mossi Feb 2 '16 at 21:32
0

I believe I have almost finished building the code! It can't parse lookups (JSON serializer somehow doesn't return fields referenced through a lookup e.g. parent__r.field__c) but here it is:

public static RecordVO[] serializeSObjects(SObject[] recs)
{
    Object[] recObjects = (Object[])JSON.deserializeUntyped(JSON.serialize(recs));
    return serializeObjects(recObjects);
}

public class RecordVO
{
    public Map<String, Object> recordFields { get; set; }
    public RecordVO[] children { get; set; }

    public RecordVO(Map<String, Object> fields, RecordVO[] ch)
    {
        recordFields = fields;
        children = ch;
    }
}

private static RecordVO[] serializeObjects(Object[] recObjects)
{
    RecordVO[] rvos = new RecordVO[]{};

    // loop over each record
    for (Object recObj : recObjects)
    {
        Map<String, Object> recordFields = new Map<String, Object>();
        RecordVO[] children = new RecordVO[]{};

        // loop over each field or children
        Map<String, Object> objMap = (Map<String, Object>)recObj;
        for (String attr : objMap.keyset())
        {
            if (attr != 'attributes')
            {
                // get children if applicable
                if (objMap.get(attr) instanceof Map<String, Object>)
                {
                    Object childObj = ((Map<String, Object>)objMap.get(attr)).get('records');
                    Object[] childObjList = (Object[])childObj;
                    if (childObjList != null)
                    {
                        RecordVO[] childrenVOs = serializeObjects(childObjList);
                        children.addAll(childrenVOs);
                    }
                }
                else
                {
                    recordFields.put(attr, objMap.get(attr));
                }
            }
        }

        rvos.add(new RecordVO(recordFields, children));
    }

    return rvos;
}

How to call:

Account[] accs = [select id, name, (select lastname, firstname from Contacts) from Account limit 2];
RecordVO[] rvos = serializeSObjects(accs);
  • I still think that getSObjects is dynamic way (and better way)to fetch child object instead of doing all this serialize / de-serialize and looping. – AtulRajguru9 Feb 4 '16 at 7:23
  • @AtulRajguru9 Down voting is used for answers that are clearly incorrect. When OP has worked out a solution to their question and confirm it's working for them then it is clearly correct. Down voting other's answers purely because they compete with yours is not the intention of this site. – Matt Lacey Feb 5 '16 at 3:22
  • ok, make sense. my bad. – AtulRajguru9 Feb 5 '16 at 3:49
  • @user1936026 right now my vote is lock down. If you edit the answer probably i will be able to correct it. thanks. – AtulRajguru9 Feb 5 '16 at 3:51
-2

To access the child/parent records dynamically you dont need to go for serialization de-serialization process you can use dynamic apex for this.

Here is how you can use dynamic apex to get map of parent objects.

Map<id,Account> accs = new Map<ID, Account>([select id, name, (select lastname, firstname from Contacts) from Account limit 2]);

sObject a = accs.get('00128000006afy0AAA');
list<sObject> obj_l;
for(Schema.ChildRelationship cr : Account.SObjectType.getDescribe().getChildRelationships()){
    //System.debug(cr.getChildSObject() + '.' + cr.getField() + ' reversed is: ' + cr.getRelationshipName());
    try{
        //System.debug(' Child Relationship : ' + cr.getRelationshipName());
        if( cr.getRelationshipName() == null) continue; 
        obj_l = a.getSObjects(cr.getRelationshipName());        
        if(obj_l != null){
            System.debug('Child contacts' + obj_l);      
        }        
    } catch(System.SObjectException e)
    {
        //Ignore this exception as we have not quried this object in SOQL
    }       
}

PS: String processing is always very CPU intensive so we should only use it as last resort.

  • 2
    What he has to be aware about contacts related list your solution. Read his question "Essentially I want to access child/parent records dynamically without having any upfront knowledge about them." – Salesforcesmarty Feb 2 '16 at 5:44
  • As mentioned by Salesforcesmarty this does not answer the question. – Mossi Feb 2 '16 at 23:31
  • see my updated answer you can access the child/parent records dynamically – AtulRajguru9 Feb 3 '16 at 4:21
  • Can you please take a moment to understand what the question was asking about before littering the board and downvoting answers? As mentioned clearly over and over I have NO UPFRONT KNOWLEDGE about the child records. In simple English, I don't know if the children are Contacts or whatever. In the code I posted (hopefully for your benefit too) I serialized the Sobjects into a simple list of wrapper objects. Instead of going records[0].getSObjects('Contacts') I can say recordVOlist[0].children. That's all I needed and my code does exactly that. – Mossi Feb 4 '16 at 7:21
  • Please take look ar when you can down vote. salesforce.stackexchange.com/help/privileges/vote-down this indeed works and so does not need to be down voted. – AtulRajguru9 Feb 5 '16 at 3:54

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.