I have a csv file which has contents like :
Fruit, Mango
Fruit, Apple
Car, Audi
Apple, Red
Color, Brown
I want to eventually convert it in a format like this :
"hierarchy" : [{
"label": "Fruit",
"children" : [ {label: "Mango"}, {label: "Apple", "children": [ {label:"Red"}]}
]
},
{
"label" : "Car",
"children" : [ {label: "Audi"}
]
},
{
"label" : "Color",
"children" : [ {label: "Brown"}
]
}]
To do this, I have inserted the values in the map :
StringBuilder sb = new StringBuilder();
String line = br.readLine();
while (line != null) {
sb.append(line);
sb.append("\n");
line = br.readLine();
}
String[] contents=(sb.toString().split("\n"));
String[] newContents;
Map<String, List<String>> myMaps = new LinkedHashMap<String, List<String>>();
for(String s : contents)
{
newContents= s.split(",");
if (!myMaps.containsKey(newContents[0])) {
myMaps.put(newContents[0], new ArrayList<String>());
}
myMaps.get(newContents[0]).add(newContents[1]);
}
This will basically convert the file to a map in the form of parent (key) and child (values). I was however wondering how to deal with the case when there is more than 1 level of depth - For example in my given csv? Will a map work in this case or is there a better approach?
Your JSON structure looks more like a tree, that can be desined as a class:
public class Node {
private String label;
private List<Node> children; // can also be of type Node[]
// getters, setters
}
then hierarchy is an array or List of nodes
List is preferred over array, because it is easier to use during population of children
UPDATE:
The full example on how to populate the Tree using Node
class:
public class Node {
private String label;
private List<Node> children = new ArrayList<>(); // to avoid checks for null
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public List<Node> getChildren() {
return children;
}
public void setChildren(List<Node> children) {
this.children = children;
}
}
class Converter {
public List<Node> fromCsvFile(String filename) throws IOException {
Node root = new Node();
BufferedReader input = new BufferedReader(new FileReader(filename));
String[] entry;
String line = input.readLine();
while (line != null) {
entry = line.split(",");
// find or create
Node node = findByLabel(root, entry[0]);
if (node == null) {
// top level
node = new Node();
node.setLabel(entry[0]);
root.getChildren().add(node);
}
// add child
Node child = new Node();
child.setLabel(entry[1]);
node.getChildren().add(child);
// next line
line = input.readLine();
}
return root.getChildren();
}
public Node findByLabel(Node node, String label) {
if (label.equals(node.getLabel())) {
return node;
}
for (Node child : node.getChildren()) {
// recursion
Node found = findByLabel(child, label);
if (found != null) {
return found;
}
}
return null;
}
}
Note: You do not need explicit Node
, and you can use Map<String, Object>
where a value can contain nested maps, or List of children, or String value for label. But using an explicit class is cleaner.
Alternative to recursive lookup for an existing node in the tree, an independent flat collection (Map) can be created to speed up lookups:
class Converter {
public List<Node> fromCsvFile(String filename) throws IOException {
Node root = new Node();
Map<String, Node> existingNodes = new HashMap<>();
BufferedReader input = new BufferedReader(new FileReader(filename));
String[] entry;
String line = input.readLine();
while (line != null) {
entry = line.split(",");
// find or create
Node node = existingNodes.get(entry[0]);
if (node == null) {
// new top level node
node = new Node();
node.setLabel(entry[0]);
root.getChildren().add(node);
existingNodes.put(entry[0], node);
}
// add child
Node child = new Node();
child.setLabel(entry[1]);
node.getChildren().add(child);
existingNodes.put(entry[1], child);
// next line
line = input.readLine();
}
return root.getChildren();
}
}
Just for reference: Full example
Map is fine. Just use recursive function to write deeper levels.
Try this.
String csv = ""
+ "Fruit, Mango\n"
+ "Fruit, Apple\n"
+ "Car, Audi\n"
+ "Apple, Red\n"
+ "Color, Brown\n"
+ "Red, Fire\n";
Set<String> topLevels = new LinkedHashSet<>();
Set<String> notTopLevels = new LinkedHashSet<>();
Map<String, Map<String, Object>> labels = new LinkedHashMap<>();
try (BufferedReader reader = new BufferedReader(new StringReader(csv))) {
String line;
while ((line = reader.readLine()) != null) {
String[] fields = line.split("\\s*,\\s*");
Map<String, Object> value = labels.computeIfAbsent(
fields[1], k -> new LinkedHashMap<String, Object>());
labels.computeIfAbsent(
fields[0], k -> new LinkedHashMap<String, Object>())
.put(fields[1], value);
topLevels.add(fields[0]);
notTopLevels.add(fields[1]);
}
}
Map<String, Object> hierarchy = new LinkedHashMap<>();
topLevels.removeAll(notTopLevels);
for (String s : topLevels)
hierarchy.put(s, labels.get(s));
System.out.println(hierarchy);
result:
{Fruit={Mango={}, Apple={Red={Fire={}}}}, Car={Audi={}}, Color={Brown={}}}