How to create a recursive method in Apex which takes a dot notation string and convert it into Hierarchical Map/Json?
Something like This probably
public with sharing class MyClass {
public static Map<String, Object> get(String input) {
if (input == null) return null;
if (!input.contains('.')) return new Map<String, Object>{input => null};
return new Map<String, Object>{
input.substringBefore('.') => get(input.substringAfter('.'))
};
}
}
Then executing:
System.debug(JSON.serialize(MyClass.get('Root.Parent.FirstChild')));
Output : {"Root":{"Parent":{"FirstChild":null}}
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'));
}
}
One of the approaches would be create a inner wrapper class for Map, and define own method to set value based on path.
Most of the code is already written in your question, so check the following pseudo-code:
public class SuperMap {
Map<String, Object> ResultMap;
public SuperMap() {
ResultMap = new Map<String, Object>();
}
public void specifyValue(String path, Object value) {
Map<String, Object> current_map = ResultMap;
List<String> path_steps = path.split('\\.');
for(Integer i = 0; i < path_steps.size() - 1; i++) {
String step = path_steps.get(i);
if (!current_map.containsKey(step)) {
current_map.put(step, (Object)new Map<String, Object>());
}
current_map = (Map<String, Object>)current_map.get(step);
}
current_map.put(path_steps.get(path_steps.size() - 1), value);
}
public String returnString() {
return JSON.serialize(ResultMap);
}
}
Example of usage:
SuperMap mp = new SuperMap();
mp.specifyValue('kuru.123.dev','Data');
mp.specifyValue('grey.123.dev2','Data2');
mp.specifyValue('grey.123.dev2','Data3');
System.debug(mp.returnString());