Convert JSONArray to Map using Jackson

drn :

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?

Mr. Polywhirl :

I would deserialize the JSON into a List of people first. After that, I would group them by name.

Main.java

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);
    }
}

Output

{Tom=[{ "name": "Tom", "age": 28 }], Bob=[{ "name": "Bob", "age": 42 }, { "name": "Bob", "age": 21 }], Mary=[{ "name": "Mary", "age": 35 }]}

Person.java

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);
    }
}

people.json

[
  { "name" : "Bob",  "age" : 42 },
  { "name" : "Bob",  "age" : 21 },
  { "name" : "Mary", "age" : 35 },
  { "name" : "Tom",  "age" : 28 }
]

Dependencies

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.9.8</version>
</dependency>

Additional Info

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.

See: https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html#toMap-java.util.function.Function-java.util.function.Function-java.util.function.BinaryOperator-

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=477642&siteId=1