将JSONArray转换为Map使用Jackson

huangapple go评论65阅读模式
英文:

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&lt;Person&gt; people = loadJSON(&quot;q61078696/people.json&quot;, Person.class);
            Map&lt;String, List&lt;Person&gt;&gt; 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(&quot;\\A&quot;).next();
        }
        return jsonString;
    }

    public static &lt;E&gt; List&lt;E&gt; loadJSON(String resourceName, Class&lt;E&gt; 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=[{ &quot;name&quot;: &quot;Tom&quot;, &quot;age&quot;: 28 }], Bob=[{ &quot;name&quot;: &quot;Bob&quot;, &quot;age&quot;: 42 }, { &quot;name&quot;: &quot;Bob&quot;, &quot;age&quot;: 21 }], Mary=[{ &quot;name&quot;: &quot;Mary&quot;, &quot;age&quot;: 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(&quot;{ \&quot;name\&quot;: \&quot;%s\&quot;, \&quot;age\&quot;: %d }&quot;, this.name, this.age);
    }
}

people.json

[
  { &quot;name&quot; : &quot;Bob&quot;,  &quot;age&quot; : 42 },
  { &quot;name&quot; : &quot;Bob&quot;,  &quot;age&quot; : 21 },
  { &quot;name&quot; : &quot;Mary&quot;, &quot;age&quot; : 35 },
  { &quot;name&quot; : &quot;Tom&quot;,  &quot;age&quot; : 28 }
]

Dependencies

&lt;dependency&gt;
  &lt;groupId&gt;com.fasterxml.jackson.core&lt;/groupId&gt;
  &lt;artifactId&gt;jackson-databind&lt;/artifactId&gt;
  &lt;version&gt;2.9.8&lt;/version&gt;
&lt;/dependency&gt;

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&lt;String, Person&gt; groupedByName = people.stream()
    .collect(Collectors.toMap(Person::getName, Function.identity()));

You can avoid this by specifying a mergeFunction

Map&lt;String, Person&gt; groupedByName = people.stream()
    .collect(Collectors.toMap(
        Person::getName,     // keyMapper
        Function.identity(), // valueMapper
        (o1, o2) -&gt; 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-

huangapple
  • 本文由 发表于 2020年4月7日 19:12:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/61078696.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定