The method JSON.deserializeUntyped works well for this type of problem. What you get returned is a map:
Map<String, Object> m = (Map<String, Object>) JSON.deserializeUntyped(jsonString);
You can use the normal map methods to get hold of the keys or to see if a key is present. So for example to access the "ideal" list items:
List<Object> ideals = (List<Object>) m.get('ideal');
if (ideals != null) {
for (Object ideal : ideals) {
String product = (String) ideal('product');
String percent = (String) ideal('percent');
String dollarAmount = (String) ideal('dollarAmount');
...
}
}
Because it is known that the data is a list (array) I've just cast the object to a list. If the content is of very unpredictable type you can also use instanceof
tests in your logic.
Here is your JSON having been formatter by http://jsonformatter.curiousconcept.com/; this sort of formatting makes the JSON easier to understand:
{
"ideal":[
{
"product":"4",
"percent":"25",
"dollarAmount":"11250"
},
{
"product":"5",
"percent":"23",
"dollarAmount":"10350"
},
{
"product":"3",
"percent":"20",
"dollarAmount":"9000"
},
{
"product":"8",
"percent":"18",
"dollarAmount":"8100"
},
{
"product":"7",
"percent":"15",
"dollarAmount":"6750"
}
],
"idealFiltered":[
{
"product":"4",
"percent":"25",
"dollarAmount":"11250"
},
{
"product":"5",
"percent":"23",
"dollarAmount":"10350"
},
{
"product":"3",
"percent":"20",
"dollarAmount":"9000"
},
{
"product":"8",
"percent":"18",
"dollarAmount":"8100"
},
{
"product":"7",
"percent":"15",
"dollarAmount":"6750"
}
],
"InBudget":[
{
"product":"4",
"percent":"70",
"dollarAmount":"7000"
},
{
"product":"8",
"percent":"30",
"dollarAmount":"3000"
}
]
}
I've done exactly this before. Here's working code for it:
public class JsonBoxer {
public Map<String, Object> root {get; private set;}
public JsonBoxer() {
this.root = new Map<String, Object>();
}
public void put(String key, Object value) {
doPut(root, key.split('\\.'), value);
}
private void doPut(Map<String, Object> currentRoot, List<String> keyChain, Object value) {
if(keyChain.size() == 1) {
currentRoot.put(keyChain[0], value);
} else {
String thisKey = keyChain.remove(0);
Map<String, Object> child = (Map<String, Object>)currentRoot.get(thisKey);
if(child == null) {
child = new Map<String, Object>();
currentRoot.put(thisKey, child);
}
doPut(child, keyChain, value);
}
}
}
Even with a test:
@IsTest
private class JsonBoxerTest {
@IsTest static void noBoxing() {
JsonBoxer boxer = new JsonBoxer();
boxer.put('a', 'b');
System.assertEquals('b', boxer.root.get('a'));
}
@IsTest static void boxing() {
JsonBoxer boxer = new JsonBoxer();
boxer.put('a.1', 'b');
System.assertEquals('b', ((Map<String, Object>)boxer.root.get('a')).get('1'));
}
@IsTest static void twoSubKeyValues() {
JsonBoxer boxer = new JsonBoxer();
boxer.put('a.1', 'b');
boxer.put('a.2', 'c');
System.assertEquals('b', ((Map<String, Object>)boxer.root.get('a')).get('1'));
System.assertEquals('c', ((Map<String, Object>)boxer.root.get('a')).get('2'));
}
@IsTest static void overwriteSubKey() {
JsonBoxer boxer = new JsonBoxer();
boxer.put('a.1', 'b');
boxer.put('a.1', 'c');
System.assertEquals('c', ((Map<String, Object>)boxer.root.get('a')).get('1'));
}
}
Best Answer
Considering your scenario, I tried creating some tree structure on my dev org. Below is the code snippet for all the components:
dynamicTreeStructure.html
dynamicTreeStructure.js
DynamicTreeStructureController.cls
Wrapper - TreeStructure.cls
Hope, I understood your use case properly and this helps you.