Jackson中Map序列化
1. 概述
在本快速教程中,我们将了解使用**Jackson 对 Java 映射进行序列化和反序列化**。 我们将说明如何在JSON 格式的字符串之间序列化和反序列化Map<String, String>、Map<Object, String>和Map<Object, Object>。
2. Maven配置
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.1</version>
</dependency>
我们可以在这里 获得最新版本的 Jackson 。
3. 序列化
序列化将 Java 对象转换为字节流,可以根据需要进行持久化或共享。Java Maps是将键对象映射到值对象的集合,通常是最不直观的序列化对象。
3.1. *Map<String, String>*序列化
对于一个简单的案例,让我们创建一个*Map<String, String>*并将其序列化为 JSON:
Map<String, String> map = new HashMap<>();
map.put("key", "value");
ObjectMapper mapper = new ObjectMapper();
String jsonResult = mapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(map);
ObjectMapper是 Jackson 的序列化映射器。它允许我们序列化我们的Map,并使用String 中的*toString()*方法将其写成漂亮打印的 JSON 字符串:
{
"key" : "value"
}
3.2. *Map<Object, String>*序列化
通过一些额外的步骤,我们还可以序列化包含自定义 Java 类的映射。让我们创建一个MyPair类来表示一对相关的String对象。 注意:getter/setter 应该是公开的,我们用*@JsonValue注解toString()以确保 Jackson 在序列化时使用这个自定义的toString()*:
public class MyPair {
private String first;
private String second;
@Override
@JsonValue
public String toString() {
return first + " and " + second;
}
// standard getter, setters, equals, hashCode, constructors
}
然后我们将告诉 Jackson 如何通过扩展 Jackson 的JsonSerializer来序列化MyPair:
public class MyPairSerializer extends JsonSerializer<MyPair> {
private ObjectMapper mapper = new ObjectMapper();
@Override
public void serialize(MyPair value,
JsonGenerator gen,
SerializerProvider serializers)
throws IOException, JsonProcessingException {
StringWriter writer = new StringWriter();
mapper.writeValue(writer, value);
gen.writeFieldName(writer.toString());
}
}
JsonSerializer顾名思义,使用MyPair的*toString()方法将 MyPair 序列化为JSON。*此外,Jackson 提供了许多Serializer 类 来满足我们的序列化要求。
接下来,我们使用 @JsonSerialize 注解将MyPairSerializer应用于我们的Map<MyPair, String>。请注意,我们只告诉 Jackson 如何序列化MyPair,因为它已经知道如何序列化String:
@JsonSerialize(keyUsing = MyPairSerializer.class)
Map<MyPair, String> map;
然后让我们测试一下我们的地图序列化:
map = new HashMap<>();
MyPair key = new MyPair("Abbott", "Costello");
map.put(key, "Comedy");
String jsonResult = mapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(map);
序列化的 JSON 输出为:
{
"Abbott and Costello" : "Comedy"
}
3.3. *Map<Object, Object>*序列化
最复杂的情况是序列化Map<Object, Object>,但大部分工作已经完成。让我们将 Jackson 的MapSerializer用于我们的Map,将上一节中的MyPairSerializer用于Map的键和值类型:
@JsonSerialize(keyUsing = MapSerializer.class)
Map<MyPair, MyPair> map;
@JsonSerialize(keyUsing = MyPairSerializer.class)
MyPair mapKey;
@JsonSerialize(keyUsing = MyPairSerializer.class)
MyPair mapValue;
然后让我们测试一下序列化我们的Map<MyPair, MyPair>:
mapKey = new MyPair("Abbott", "Costello");
mapValue = new MyPair("Comedy", "1940s");
map.put(mapKey, mapValue);
String jsonResult = mapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(map);
使用MyPair的*toString()*方法的序列化 JSON 输出是:
{
"Abbott and Costello" : "Comedy and 1940s"
}
4. 反序列化
反序列化将字节流转换为我们可以在代码中使用的 Java 对象。在本节中,我们将把 JSON 输入反序列化为不同签名的Map。
4.1. *Map<String, String>*反序列化
对于一个简单的例子,让我们采用 JSON 格式的输入字符串并将其转换为Map<String, String> Java 集合:
String jsonInput = "{\"key\": \"value\"}";
TypeReference<HashMap<String, String>> typeRef
= new TypeReference<HashMap<String, String>>() {};
Map<String, String> map = mapper.readValue(jsonInput, typeRef);
我们使用 Jackson 的ObjectMapper,就像我们对序列化所做的那样,使用readValue()来处理输入。另外,请注意我们对 Jackson 的TypeReference的使用,我们将在所有反序列化示例中使用它来描述目标Map的类型。这是我们地图的*toString()*表示:
{key=value}
4.2. *Map<Object, String>*反序列化
现在让我们将输入 JSON 和目的地的TypeReference更改为Map<MyPair, String>:
String jsonInput = "{\"Abbott and Costello\" : \"Comedy\"}";
TypeReference<HashMap<MyPair, String>> typeRef
= new TypeReference<HashMap<MyPair, String>>() {};
Map<MyPair,String> map = mapper.readValue(jsonInput, typeRef);
我们需要为MyPair创建一个构造函数,它接受一个包含两个元素的String并将它们解析为MyPair元素:
public MyPair(String both) {
String[] pairs = both.split("and");
this.first = pairs[0].trim();
this.second = pairs[1].trim();
}
我们的*Map<MyPair,String>对象的toString()*是:
{Abbott and Costello=Comedy}
当我们反序列化为包含Map 的 Java 类时,还有另一种选择;我们可以使用 Jackson 的KeyDeserializer类,它是 Jackson 提供的众多反序列化类 之一。让我们用*@JsonCreator*、@JsonProperty和*@JsonDeserialize* 注解我们的ClassWithAMap:
public class ClassWithAMap {
@JsonProperty("map")
@JsonDeserialize(keyUsing = MyPairDeserializer.class)
private Map<MyPair, String> map;
@JsonCreator
public ClassWithAMap(Map<MyPair, String> map) {
this.map = map;
}
// public getters/setters omitted
}
在这里,我们告诉 Jackson 反序列化ClassWithAMap中包含的Map<MyPair, String>,因此我们需要扩展KeyDeserializer来描述如何从输入String反序列化映射的键,即MyPair对象:
public class MyPairDeserializer extends KeyDeserializer {
@Override
public MyPair deserializeKey(
String key,
DeserializationContext ctxt) throws IOException,
JsonProcessingException {
return new MyPair(key);
}
}
然后我们可以使用readValue测试反序列化:
String jsonInput = "{\"Abbott and Costello\":\"Comedy\"}";
ClassWithAMap classWithMap = mapper.readValue(jsonInput,
ClassWithAMap.class);
同样,ClassWithAMap映射的*toString()*方法为我们提供了我们期望的输出:
{Abbott and Costello=Comedy}
4.3. *Map<Object,Object>*反序列化
最后,让我们将输入 JSON 和目的地的TypeReference更改为Map<MyPair, MyPair>:
String jsonInput = "{\"Abbott and Costello\" : \"Comedy and 1940s\"}";
TypeReference<HashMap<MyPair, MyPair>> typeRef
= new TypeReference<HashMap<MyPair, MyPair>>() {};
Map<MyPair,MyPair> map = mapper.readValue(jsonInput, typeRef);
我们的*Map<MyPair, MyPair>对象的toString()*是:
{Abbott and Costello=Comedy and 1940s}