1. 概述
在这个教程中,我们将探讨将Jackson的原始数据类型JsonNode转换为Java集合的不同方法。虽然我们可以直接使用JsonNode读取JSON,但将其转换为Java集合是有益的。Java集合提供了类型安全、更快的处理速度以及更多针对特定类型的操作优势。
2. 示例设置
在我们的代码示例中,我们将查看将JsonNode转换为对象列表或映射的各种方式。让我们先构建示例的基础结构。
2.1. 依赖项
首先,我们在pom.xml文件中添加Jackson核心库的依赖:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.17.0</version>
</dependency>
2.2. JSON数据
接下来,我们定义一个用于我们示例的JSON:
{
"persons": [
{
"name": "John",
"age": 30
},
{
"name": "Alice",
"age": 25
}
],
"idToPerson" : {
"1234": {
"name": "John",
"age": 30
},
"1235": {
"name": "Alice",
"age": 25
}
}
}
在这个JSON中,我们有一个名为persons
的JSON数组和一个名为idToPerson
的JSON对象。我们将探讨如何将它们转换为Java集合。
2.3. DTO(数据传输对象)
现在,我们定义一个名为Person
的类,它将在示例中使用:
public class Person {
private String name;
private int age;
// constructors/getters/setters
}
2.4. 将JSON字符串转换为JsonNode
如果我们要读取整个JSON对象,可以使用Jackson的ObjectMapper类:
JsonNode rootNode = new ObjectMapper().readTree(jsonString);
JsonNode childNode = rootNode.get("persons");
*要将整个JSON转换为JsonNode对象,我们可以使用readTree()方法。然后,我们使用get()方法遍历JsonNode对象,获取指定名称的嵌套对象。*
3. 手动转换
在查看库方法之前,让我们先看看如何手动将JsonNode转换为集合。
3.1. 手动将JsonNode转换为列表
要将JsonNode转换为列表,我们可以逐条遍历并创建一个包含它的列表:
List<Person> manualJsonNodeToList(JsonNode personsNode) {
List<Person> people = new ArrayList<>();
for (JsonNode node : personsNode) {
Person person = new Person(node.get("name").asText(), node.get("age").asInt());
people.add(person);
}
return people;
}
这里,我们使用循环遍历输入节点的所有子节点。这只有在输入节点是数组时才可行。对于每个节点,我们创建一个Person
对象,并将其添加到列表中。我们使用get(fieldName)
方法从节点获取name
和age
。JsonNode提供了各种方法将返回值转换为基本的Java类型。在这里,asText()
和asInt()
方法分别将值转换为String
和int
。
3.2. 手动将JsonNode转换为映射
接下来,让我们看看映射的类似转换:
Map<String, Person> manualJsonNodeToMap(JsonNode idToPersonsNode) {
Map<String, Person> mapOfIdToPerson = new HashMap<>();
idToPersonsNode.fields()
.forEachRemaining(node -> mapOfIdToPerson.put(node.getKey(),
new Person(node.getValue().get("name").asText(), node.getValue().get("age").asInt())));
return mapOfIdToPerson;
}
这里,我们使用fields()
方法迭代映射条目。它返回一个Iterator<Map.Entry<String, JsonNode>>
对象,我们可以进一步处理。然后,我们读取每个条目并将它放入我们的映射中。
4. 使用Jackson的readValue()和convertValue()
Jackson提供了多种方法将JsonNode转换为Java对象。让我们看看其中的两个。
4.1. 使用readValue()
可以使用readValue()方法,通过TypeReference将JsonNode转换为列表或映射:
List<Person> readValueJsonNodeToList(JsonNode personsNode) throws IOException {
TypeReference<List<Person>> typeReferenceList = new TypeReference<List<Person>>() {};
return new ObjectMapper().readValue(personsNode.traverse(), typeReferenceList);
}
Map<String, Person> readValueJsonNodeToMap(JsonNode idToPersonsNode) throws IOException {
TypeReference<Map<String, Person>> typeReferenceMap = new TypeReference<Map<String, Person>>() {};
return new ObjectMapper().readValue(idToPersonsNode.traverse(), typeReferenceMap);
}
首先,我们创建一个TypeReference对象,传入我们需要转换的确切类型。然后,我们调用readValue()方法,其中JsonParser由jsonNode.traverse()
提供。使用解析器,根据提供的TypeReference,它将节点解串化为列表或映射。
4.2. 使用convertValue()
同样,我们可以使用*convertValue()*方法:
List<Person> convertValueJsonNodeToList(JsonNode personsNode) {
TypeReference<List<Person>> typeReferenceList = new TypeReference<List<Person>>() {};
return new ObjectMapper().convertValue(personsNode, typeReferenceList);
}
Map<String, Person> convertValueJsonNodeToMap(JsonNode idToPersonsNode) {
TypeReference<Map<String, Person>> typeReferenceMap = new TypeReference<Map<String, Person>>() {};
return new ObjectMapper().convertValue(idToPersonsNode, typeReferenceMap);
}
convertValue()方法的工作原理是先序列化输入对象,然后将其反序列化为目标类型。因此,它更灵活,可用于从一个对象到另一个对象的转换。例如,我们也可以用它将Java对象反向转换为JsonNode。
5. 定制反序列化器
我们还可以提供一个自定义反序列化器来执行转换。让我们看看如何定义一个:
public class CustomPersonListDeserializer extends JsonDeserializer<List<Person>> {
@Override
public List<Person> deserialize(com.fasterxml.jackson.core.JsonParser p,
com.fasterxml.jackson.databind.DeserializationContext ctxt) throws IOException {
ObjectMapper objectMapper = (ObjectMapper) p.getCodec();
List<Person> personList = new ArrayList<>();
JsonNode personsNode = objectMapper.readTree(p);
for (JsonNode node : personsNode) {
personList.add(objectMapper.readValue(node.traverse(), Person.class));
}
return personList;
}
}
让我们看看代码中的一些关键部分:
- 首先,类继承了Jackson的JsonDeserializer。
- 然后,我们重写
deserialize()
方法并提供实现。 - 在实现中,我们从JsonParser对象获取ObjectMapper。
-
objectMapper.readTree()
将解析器表示的整个树转换为一个JsonNode实例。 - 最后,类似于手动转换,我们通过遍历JSON数组中的每个节点,将每个节点转换为一个
Person
对象。
反序列化器的工作方式与其他方法相似,但提供了一种职责分离。此外,自定义反序列化器提供了灵活性,因为我们在调用代码中可以轻松切换不同的反序列化器。
在下一节中,我们将看到如何在测试代码时使用这个反序列化器。
6. 测试
现在我们的不同方法都准备好了,让我们编写一些测试来验证它们。
6.1. 设置
让我们设置测试类:
public class JsonNodeToCollectionUnitTest {
public static String jsonString = "{\"persons\":[{\"name\":\"John\",\"age\":30},{\"name\":\"Alice\",\"age\":25}],\"idToPerson\":{\"1234\":{\"name\":\"John\",\"age\":30},\"1235\":{\"name\":\"Alice\",\"age\":25}}}";
static JsonNode completeJson;
static JsonNode personsNode;
static JsonNode idToPersonNode;
@BeforeAll
static void setup() throws JsonProcessingException {
completeJson = new ObjectMapper().readTree(jsonString);
personsNode = completeJson.get("persons");
idToPersonNode = completeJson.get("idToPerson");
}
}
这里,我们定义了一个JSON字符串,作为测试输入。然后,我们定义了一个setup()
方法,在所有测试之前执行。它设置了我们的输入JsonNode对象。
6.2. 测试转换方法
接下来,让我们测试转换方法:
@Test
void givenJsonNode_whenConvertingToList_thenFieldsAreCorrect() throws IOException {
List<Person> personList1 = JsonNodeConversionUtil.manualJsonNodeToList(personsNode);
List<Person> personList2 = JsonNodeConversionUtil.readValueJsonNodeToList(personsNode);
List<Person> personList3 = JsonNodeConversionUtil.convertValueJsonNodeToList(personsNode);
validateList(personList1);
validateList(personList2);
validateList(personList3);
}
private void validateList(List<Person> personList) {
assertEquals(2, personList.size());
Person person1 = personList.get(0);
assertEquals("John", person1.getName());
assertEquals(30, person1.getAge());
Person person2 = personList.get(1);
assertEquals("Alice", person2.getName());
assertEquals(25, person2.getAge());
}
这里,我们调用每个方法并验证返回列表的内容符合预期。
我们为映射转换添加一个类似的测试:
@Test
void givenJsonNode_whenConvertingToMap_thenFieldsAreCorrect() throws IOException {
Map<String, Person> personMap1 = JsonNodeConversionUtil.manualJsonNodeToMap(idToPersonNode);
Map<String, Person> personMap2 = JsonNodeConversionUtil.readValueJsonNodeToMap(idToPersonNode);
Map<String, Person> personMap3 = JsonNodeConversionUtil.convertValueJsonNodeToMap(idToPersonNode);
validateMapOfPersons(personMap1);
validateMapOfPersons(personMap2);
validateMapOfPersons(personMap3);
}
private void validateMapOfPersons(Map<String, Person> map) {
assertEquals(2, map.size());
Person person1 = map.get("1234");
assertEquals("John", person1.getName());
assertEquals(30, person1.getAge());
Person person2 = map.get("1235");
assertEquals("Alice", person2.getName());
assertEquals(25, person2.getAge());
}
6.3. 测试自定义反序列化器
现在来看看如何使用自定义反序列化器,然后比较结果:
@Test
void givenJsonNode_whenConvertingToListWithCustomDeserializer_thenFieldsAreCorrect() throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(List.class, new CustomPersonListDeserializer());
objectMapper.registerModule(module);
List<Person> personList = objectMapper.convertValue(personsNode, new TypeReference<List<Person>>() {
});
validateList(personList);
}
在这里,我们首先使用SimpleModule类将CustomPersonListDeserializer包装在一个模块中。然后,我们使用该模块注册到ObjectMapper实例中。这样,我们就指示ObjectMapper在需要解序列化列表时使用CustomPersonListDeserializer。
当我们接下来调用convertValue()
方法时,将使用反序列化器返回输出列表。
7. 结论
在这篇文章中,我们探讨了如何将Jackson的JsonNode转换为像列表和映射这样的类型化的Java集合。我们讨论了执行此操作的不同方法,并测试了它们都能得到相同的结果。
对于小规模转换,我们可能使用手动方法。随着JSON大小的增长,使用Jackson库的方法会更好。如果需要更复杂的转换,提供自定义反序列化器会更合适。
如往常一样,示例代码的源代码可以在GitHub上找到。