Create a nested map from Json using streams

Viewed 99

I have the below Json that I'm reading into nested POJOs with the same structure.

{
  "employees": [
    {
      "name": "John",
      "age": "30",
      "proData": [
        {
          "year": "1",
          "idList": [
            "234342",
            "532542",
            "325424",
            "234234"
          ]
        },
        {
          "year": "2",
          "idList": [
            "234342",
            "532542",
            "325424",
            "234234"
          ]
        },
        {
          "year": "3",
          "idList": [
            "234342",
            "532542",
            "325424",
            "234234"
          ]
        }
      ]
    },
    {
      "name": "Scott",
      "age": "32",
      "proData": [
        {
          "year": "1",
          "idList": [
            "234342",
            "532542",
            "325424",
            "234234"
          ]
        },
        {
          "year": "2",
          "idList": [
            "234342",
            "532542",
            "325424",
            "234234"
          ]
        },
        {
          "year": "3",
          "idList": [
            "234342",
            "532542",
            "325424",
            "234234"
          ]
        }
      ]
    }
  ]
}

Now I wanted to map this to a structure like below, the ProData can be initialized using each of the string in the idList.

Map<String,Map<String,List<ProData>>> finalMap

I have written something like the below and it works.

        Map<String,Map<String,List<ProData>>> finalMap = new HashMap<>();

        for(Employee employee:root.getEmployees()){
            Map<String,List<ProData>> proDataMap = new HashMap<>();
            for(ProData proData: employee.getProData()){
                List<ProData> finalObjs = new ArrayList<>();
                for(String id:proData.getIdList()){
                   finalObjs.add(new ProData(id));
                }

                proDataMap.put(proData.getYear(),finalObjs);
            }
            finalMap.put(employee.getName(),proDataMap);
        }

I wanted to make a better version using the stream API.

2 Answers

The end result is a map, so use the toMap collector. The maps' keys are the employee names (assuming no duplicates), and the map values require a little bit more work.

root.getEmployees().stream().collect(
    Collectors.toMap(
        Employee::getName,
        Employee::getProDataMap
    )
}

Now let's try writing getProDataMap in Employee. Again we use the toMap collector. The keys are the years (assuming no duplicates), and the values are the id lists mapped to ProData using the constructor.

public Map<String, List<ProData>> getProDataMap() {
    return this.getProData().stream().collect(
        Collectors.toMap(
            ProData::getYear,
            proData -> proData.getIdList().stream()
                .map(ProData::new)
                .collect(Collectors.toList())
        )
    )
}

should be something similar to,

root.stream().forEach(employee -> {
                Map<String,List<ProData>> proDataMap = new HashMap<>();
                employee.getProdData().stream().forEach(data -> {
                    List<ProData> finalObjs = new ArrayList<>();
                    data.getIdList().stream().forEach(data -> {
                        finalObjs.add(new ProData(id));
                    });
                });
            });

but regardless you can also use Gson to parse it https://mkyong.com/java/how-to-parse-json-with-gson/

Related