1

I have a String input in the following format:

Input String: [{ "id":"1", "name":"A", "address":"St 1"},{ "id":"2", "name":"B", "address":"St 2"}, ...]

And I want to be able to convert this to a Map<String, Map<String, String>> format. So, something like:

Required Output Format: {1: {id:1, name:"A", address: "St 1"} 2: {id:2, name:"B", address: "St 2"}}

I created a class to help in parsing the input:

public class Student{
  private String id;
  private String name;
  private String address;
}

Which I am trying to do through Jackson's ObjectMapper to get the data in the format: List<Student> format and then use Collectors.toMap() to convert it to a Map of Maps format.

All the examples I have seen so far suggest an approach like:

List<Student> student = objectMapper.readValue(inputString, new TypeReference<List<Student>>(){});

Map<String, Student> studentTempMap = student.stream()
    .collect(Collectors.toMap(
        Student::getId,
        Function.identity()
    ));

Which makes the studentTempMap something like:

{ 1: object of Student("id":"1", "name":"A", "address":"St 1"),
  2: object of Student("id":"2", "name":"B", "address":"St 2"), 
  ... }

A brute force approach from here:

  1. create a new Map studentMap
  2. Iterate over keys (which are "id") in studentTempMap.
    • Then create another Map, temp.
    • Add keys "id", "name", and "address" and values using something like studentTempMap.get(2).get("id"), and something similar for all the other keys (name and address). Where 2 would be the current iterator key over the Map studentTempMap.
    • Finally add a key say as 2 (current iterator) and value temp in the studentMap.

I do not want to use this brute force approach as I have a large number of Student objects.

Is there a way through ObjectMapper to get the output directly in the form of Map<String, Map<String, String>> format? Or is there a way through Collectors.toMap to parse it to the above format?

1 Answer 1

2

I want to be able to convert this to a Map<String, Map<String, String>>

If you want to obtain a nested map of strings Map<String,Map<String,String>> as a result, you don't need to convert JSON into a list of POJO.

Instead, you can parse JSON into a list of maps List<Map<String,String>> and then generate a nested map.

String inputString = """
    [{ "id":"1", "name":"A", "address":"St 1"},
     { "id":"2", "name":"B", "address":"St 2"}]""";
    
ObjectMapper objectMapper = new ObjectMapper();
List<Map<String, String>> students = objectMapper.readValue(
    inputString, new TypeReference<>() {}
);
        
Map<String, Map<String, String>> studentMapById = students.stream()
    .collect(Collectors.toMap(
        map -> map.get("id"), // key 
        Function.identity(),  // value
        (left, right) -> left // resolving duplicates
    ));
    
studentMapById.forEach((k, v) -> System.out.println(k + " : " + v));

Output:

1 : {id=1, name=A, address=St 1}
2 : {id=2, name=B, address=St 2}
Sign up to request clarification or add additional context in comments.

4 Comments

Great solution. But there is one compile time error in new TypeReference<> as cannot use '<>' with anonymous inner classes.
@girdhar There's no compilation error - I've tested this solution. And there's no issue with using diamond operator and anonymous classes.
Yes you are right, actually I am using JDK 1.8 and that's why I am getting this error. This change for anonymous classes has been done in JDK 1.9 onwards. "Java 9 – Diamond operator enhancements"
@girdhar "I am using JDK 1.8" - you've missed a couple of LTS versions. Anyway, even with JDK 8 adding a type variable isn't a big deal.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.