【问题标题】:Convert JSONArray to Map using Jackson使用 Jackson 将 JSONArray 转换为 Map
【发布时间】:2020-04-07 11:12:20
【问题描述】:

在我们的旧代码中,我们以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=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,但没有成功。

这可能吗,怎么做?

【问题讨论】:

  • 您的示例不完整,请遵守 Java 的正确命名约定。
  • 我可以知道我的例子中什么不完整吗?谢谢
  • 什么是 JSON 输入的实际示例?比如,哪里有倍数?此外,如果您使用标准集合,请使用Map 作为HashMap 的类型,因为这是更标准的做法。它只是众多实现中的一种,因此在这种情况下实现无关紧要。最后,如果两个人确实有相同的名字,那么当然会覆盖其中一个。这就是地图的工作原理。
  • 示例 json 输入是我上面提到的 jsonArray 对象的值。我希望将 jsonArray 的值转换为 Map 映射,因为我将在我的请求方法中传递映射。我的最终目标是想出与 jsonArray 值相似的地图对象。
  • List&lt;Person&gt; 的示例是什么样的?这是你开始的吗?你把它作为 JSON 拿回来了吗?为什么要将它映射到JSONArray 而不是List&lt;Person&gt;?如果您已经有 POJO,为什么还需要 JSON 库?

标签: java json jackson hashmap


【解决方案1】:

我会先将 JSON 反序列化为 List 的人。之后,我会按名称对它们进行分组。

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

输出

{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 }
]

依赖关系

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

附加信息

如果您想按名称映射并避免分组(忽略欺骗)。

这会抛出一个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 (keep the first occurrence)
        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-

【讨论】:

  • 我们可以唤醒 groupByName Map 吗?和 value 应该是与 people.json 等效的地图,而不是按名称分组。
  • @drn 那么到时候你想分组什么?地图需要钥匙;你的钥匙是什么?
猜你喜欢
  • 2015-06-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多