英文:
Convert JSONArray to Map using Jackson
问题
在我们的遗留代码中,我们以HashMap
的形式发送请求主体,这是我无法更改的,因为其他应用可能会受到影响。
示例HashMap值 = {name=name1, age=age1}
然而,如果在我的请求中有多个JSON对象,我在使用HashMap
时会遇到问题,例如:
HashMap<String, Object> map = new HashMap<String, Object>();
for (Person person: Persons) {
map.put("name", "name1");
map.put("age", "age1");
}
如果有2个或更多人,只有最后一个人的姓名和年龄会被放入Map
,因为第一个人的姓名和年龄会被覆盖,因为它们有相同的键("name"和"age")。
我期望的Map
值是[{name=name1, age=age1}, {name=name2, age=age2}]
,但我只得到了{name=name2, age=age2}
。
我所做的是,在每次循环中,我将其放入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);
}
因此,当我打印JSONArray
时,它是:
[{"name":"name1", "age":"age1"}, {"name":"name2", "age":"age2"}]
但是,我需要将其转换为HashMap
,以便将其作为参数传递给oursendRequest()
方法。
我尝试过使用Jackson中的ObjectMapper
,但没有成功。
这是否可能,如何实现?
英文:
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?
答案1
得分: 1
我首先会将 JSON 反序列化为一个人员列表。之后,我会按名称对它们进行分组。
## Main.java
```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
如果您想按名称映射并避免分组(忽略重复项)。
这将抛出 IllegalStateException
,因为存在重复的键,为了避免这种情况,您需要访问该映射。
Map<String, Person> groupedByName = people.stream()
.collect(Collectors.toMap(Person::getName, Function.identity()));
您可以通过在第四个参数中指定一个 mergeFunction
来避免这种情况。
Map<String, Person> groupedByName = people.stream()
.collect(Collectors.toMap(
Person::getName, // keyMapper
Function.identity(), // valueMapper
(o1, o2) -> o1, // mergeFunction(保留第一个出现的项)
TreeMap::new) // mapSupplier
);
您还可以在第四个参数中指定一个 supplier
。我选择了一个 TreeMap
来保持名称键的顺序。
请参阅: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-
<details>
<summary>英文:</summary>
I would deserialize the JSON into a `List` of people first. After that, I would group them by name.
## Main.java
```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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论