Reputation: 31
In our legacy code, we are sending request body in a form of a HashMap
, something I cannot change because other applications may be affected.
Sample Hashmap value = {name=name1, age=age1}
However I have problem on using HashMap
if there are multiple JSON objects in my request, for example
HashMap<String, Object> map = new HashMap<String, Object>();
for (Person person: Persons) {
map.put("name", "name1");
map.put("age", "age1");
}
If there are 2 or more people, only the last person's name and age will be put in the Map
, because the first person's name and age are overridden because they have the same key ("name" and "age").
My desired map value was [{name=name1, age=age1}, {name=name2, age=age2}]
, but I only got {name=name2, age=age2}
What I did is, in every loop, I put it in JSONArray
:
JSONArray jsonArray = new jsonArray();
for (Person person: Persons) {
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("name", "name1");
map.put("age", "age1");
jsonArray.put(map);
}
So when I print JSONArray
, it is:
[{"name":"name1", "age":"age1"}, {"name":"name2", "age":"age2"}]
But again, I need to transform this into HashMap
so I can pass it as parameter to oursendRequest()
method.
I tried to use the ObjectMapper
in Jackson, but it didn't work.
Is this possible, and how?
Upvotes: 1
Views: 2470
Reputation: 48723
I would deserialize the JSON into a List
of people first. After that, I would group them by name.
package q61078696;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.*;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
try {
List<Person> people = loadJSON("q61078696/people.json", Person.class);
Map<String, List<Person>> groupedByName = people.stream()
.collect(Collectors.groupingBy(Person::getName));
System.out.println(groupedByName);
} catch (Exception e) {
e.printStackTrace();
}
}
public static String loadJSON(String resourceName) {
InputStream is = Main.class.getClassLoader().getResourceAsStream(resourceName);
String jsonString = null;
try (Scanner scanner = new Scanner(is, StandardCharsets.UTF_8.name())) {
jsonString = scanner.useDelimiter("\\A").next();
}
return jsonString;
}
public static <E> List<E> loadJSON(String resourceName, Class<E> clazz) throws IOException {
ObjectMapper mapper = new ObjectMapper();
String jsonString = loadJSON(resourceName);
CollectionType typeReference = TypeFactory.defaultInstance().constructCollectionType(List.class, clazz);
return mapper.readValue(jsonString, typeReference);
}
}
{Tom=[{ "name": "Tom", "age": 28 }], Bob=[{ "name": "Bob", "age": 42 }, { "name": "Bob", "age": 21 }], Mary=[{ "name": "Mary", "age": 35 }]}
package q61078696;
public class Person {
private String name;
private int age;
public Person() {
this(null, 0);
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return String.format("{ \"name\": \"%s\", \"age\": %d }", this.name, this.age);
}
}
[
{ "name" : "Bob", "age" : 42 },
{ "name" : "Bob", "age" : 21 },
{ "name" : "Mary", "age" : 35 },
{ "name" : "Tom", "age" : 28 }
]
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
If you want to map by name and avoid grouping (ignoring dupes).
This will throw an IllegalStateException
, because there are duplicate keys, to avoid this, you will need to access the map.
Map<String, Person> groupedByName = people.stream()
.collect(Collectors.toMap(Person::getName, Function.identity()));
You can avoid this by specifying a mergeFunction
Map<String, Person> groupedByName = people.stream()
.collect(Collectors.toMap(
Person::getName, // keyMapper
Function.identity(), // valueMapper
(o1, o2) -> o1, // mergeFunction (keep the first occurrence)
TreeMap::new) // mapSupplier
);
You can also specify a supplier
in the 4th parameter. I chose a TreeMap
to keep the name keys in order.
Upvotes: 1