I have a use case where I need to dynamically create a JSON.

Below is an idea of what a finished JSON will look like:

var treeData = [{
Name: "Johnson Holding Company",
Id: '1',
children: [
    {Name: "Johnson Metals",
     Id: '2',
     parentId: '1'
    {Name: "Johnson Plastics",
     Id: '3', 
     parentId: '1', 
     children: [
         {Name: "Johnson Little Plastics", 
          Id: '4',
          parentId: '3',
          children: [
              {Name: "Johnson Little Box",
               Id: '5',
               parentId: '4'

As you can see the account is related to the parent by the parentId field. I can work out the logic to get a grandchild account nested but the problem I'm having is I need to be able to make a list of the account is for the newest child accounts, see if there are any other children accounts and if there are then nest them where appropriate.

Any help would be much appreciated. Again the biggest thing I need help with is once I get the grandChildren records and add them to the treeData in the next method, How would I loop the logic to keep checking new account ids and seeing if they have children and then add them to the parent account in the treeData JSON until no new accounts are found?

1 Answer 1


Considering your scenario, I tried creating some tree structure on my dev org. Below is the code snippet for all the components:


<lightning-card title="Tree Components with mutliple nested Accounts">
  <div class="slds-m-top_medium slds-m-bottom_x-large">
    <!-- Simple -->
    <template if:true={accounts.data}>
      <div class="slds-p-around_medium lgc-bg">
        <lightning-tree items={accounts.data} header="Accounts"></lightning-tree>


 import { LightningElement, wire } from 'lwc';
 import getAccounts from '@salesforce/apex/DynamicTreeStructureController.getAccounts';

 export default class Dynamic_Tree_Structure extends LightningElement {
   @wire(getAccounts) accounts;


    public with sharing class DynamicTreeStructureController {
    public DynamicTreeStructureController() {


    private static Map<Id, TreeStructure> result;
    private static Map<Id, Id> childIdMap;

    Static Method to be fed in @wire for LWC
    public static List<TreeStructure> getAccounts(){
        result = new Map<Id, TreeStructure>();
        childIdMap = new Map<Id, Id>();
        Map<Id, Account> accMap = new Map<Id, Account>([SELECT Id, Name FROM Account WHERE ParentId = null]);
        return result.values();

    * Recursion method to get all levels of accounts and their related records
    private static List<TreeStructure> startFetchingAccountDetails(Map<Id, Account> accMap){
        Map<Id, TreeStructure> parentStructure = gatherAllAccountInformation(accMap);

        //attach the first level to actual result and rest will auotmatically link
        //due to pass by reference way
        if(result == null || result.isEmpty()){
        Map<Id, Account> childMap = new Map<Id, Account>([SELECT Id, Name, DemoLight12__Account__c FROM Account WHERE DemoLight12__Account__c =: accMap.keySet()]);
        if(childMap != null && !childMap.isEmpty() && childMap.size() > 0){
            Map<Id, Id> accChildIdMap = new Map<Id, Id>();
            for(Id childAccountId : childMap.keySet()){
                Account child = childMap.get(childAccountId);
                childIdMap.put(child.Id, child.DemoLight12__Account__c);

            //run this method recursively to get all child levels.
            List<TreeStructure> childStructure = startFetchingAccountDetails(childMap);
            for(TreeStructure child : childStructure){
                TreeStructure parent = parentStructure.get(childIdMap.get(child.name));
        return parentStructure.values();

    * Method to gather all information for all accounts recieved
    private static Map<Id, TreeStructure> gatherAllAccountInformation( Map<Id, Account> accMap){
        Map<Id, TreeStructure> result = new Map<Id, TreeStructure>();

        Map<Id, List<Contact>> accConMap = new  Map<Id, List<Contact>>();
        Map<Id, List<Opportunity>> accOppCMap = new Map<Id, List<Opportunity>>();
        Map<Id, List<Case>> conCaseCMap = new Map<Id, List<Case>>();

        //gather all contacts
        for(Contact con : [SELECT Id, Name, AccountId FROM Contact WHERE AccountId =: accMap.keySet()]){
                accConMap.put(con.AccountId, new List<Contact>());

        //gather all cases
        for(Case cas : [SELECT Id, CaseNumber, ContactId FROM Case WHERE ContactId =: accConMap.keySet()]){
                conCaseCMap.put(cas.ContactId, new List<Case>());

        for(Id accountId : accMap.keySet()){
            Account acc = accMap.get(accountId);
            TreeStructure accStructure = new TreeStructure(acc.name, accountId, false, null);

            //add all contacts if present
                TreeStructure conStructure = new TreeStructure('Contacts', 'Contacts', false, null);
                for(Contact con :  accConMap.get(accountId)){
                    conStructure.items.add( new TreeStructure(con.Name, con.Id, false, null));
                        TreeStructure caseStructure = new TreeStructure('Cases', 'Cases', false, null);
                        for(Case cas : conCaseCMap.get(con.Id)){
                            caseStructure.items.add( new TreeStructure(cas.CaseNumber, cas.Id, false, null));

            result.put(accountId, accStructure);
        return result;

Wrapper - TreeStructure.cls

    public class TreeStructure{
    @AuraEnabled public String label;
    @AuraEnabled public String name;
    @AuraEnabled public Boolean expanded;
    @AuraEnabled public List<TreeStructure> items;
    public TreeStructure(String label, String name, Boolean expanded, List<TreeStructure> items){
        this.label = label;
        this.name = name;
        this.expanded = expanded;
        if(items != null && items.size() > 0){
            this.items = items;
            this.items = new List<TreeStructure>();

enter image description here

Hope, I understood your use case properly and this helps you.

  • Thank you for taking the time to do this. I will take a look and see if it is applicable to my use case. I was trying to do heavy js for this but I hadn't considered doing most logic in Apex like you have done here.
    – Benton
    Commented Apr 13, 2022 at 19:01
  • Can you please explain what DemoLight12__Account__c field is?
    – Benton
    Commented Apr 13, 2022 at 19:44
  • You can ignore that my dev org has Namespace added, so whenever I am creating a new field or any object Namespace is added as Prefix. So, the custom field is Account__c which is self lookup on the Account object. Commented Apr 13, 2022 at 20:06
  • Ok thank you. One more question: Could you briefly explain why the selflookup to Account is needed?
    – Benton
    Commented Apr 13, 2022 at 20:08
  • SelfLookup is needed to have a Parent-Child relationship on the same object. You can get better understanding regarding SelfLookup relationship from below trailhead link. trailhead.salesforce.com/en/content/learn/projects/… Commented Apr 13, 2022 at 20:11

You must log in to answer this question.

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