Map Serialization and Deserialization with Jackson – 用杰克逊进行地图序列化和反序列化

最后修改: 2017年 4月 1日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this quick tutorial, we’ll look at the serialization and deserialization of Java maps using Jackson.

在这个快速教程中,我们将看看使用Jackson对Java地图进行序列化和反序列化。

We’ll illustrate how to serialize and deserialize Map<String, String>, Map<Object, String>, and Map<Object, Object> to and from JSON-formatted Strings.

我们将说明如何将Map<String, String>Map<Object, String>Map<Object, Object> 串联和反串联到JSON格式的字符串。

2. Maven Configuration

2.Maven配置

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

We can get the latest version of Jackson here.

我们可以在这里获得杰克逊的最新版本

3. Serialization

3.序列化

Serialization converts a Java object into a stream of bytes, which can be persisted or shared as needed. Java Maps are collections that map a key Object to a value Object, and are often the least intuitive objects to serialize.

序列化将Java对象转换为字节流,可以根据需要进行持久化或共享。Java Maps是将一个键Object映射到一个值Object的集合,并且通常是最不直观的序列化对象。

3.1. Map<String, String> Serialization

3.1.Map<String, String>序列化

For a simple case, let’s create a Map<String, String> and serialize it to JSON:

对于一个简单的案例,让我们创建一个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 is Jackson’s serialization mapper. It allows us to serialize our map, and write it out as a pretty-printed JSON String using the toString() method in String:

ObjectMapper 是 Jackson 的序列化映射器。它允许我们序列化我们的map,,并使用String中的toString()方法将其写成一个漂亮的JSON。

{
  "key" : "value"
}

3.2. Map<Object, String> Serialization

3.2.Map<Object, String>序列化

With a few extra steps, we can also serialize a map containing a custom Java class. Let’s create a MyPair class to represent a pair of related String objects.

通过一些额外的步骤,我们也可以序列化一个包含自定义Java类的地图。让我们创建一个MyPair类来表示一对相关的String对象。

Note: the getters/setters should be public, and we annotate toString() with @JsonValue to ensure Jackson uses this custom toString() when serializing:

注意:getters/setters应该是公共的,我们用@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
}

Then we’ll tell Jackson how to serialize MyPair by extending Jackson’s JsonSerializer:

然后我们将告诉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, as the name suggests, serializes MyPair to JSON using MyPair‘s toString() method. Furthermore, Jackson provides many Serializer classes to fit our serialization requirements.

JsonSerializer,顾名思义,使用MyPairtoString()方法将MyPair序列化为JSON。此外,Jackson提供了许多序列化器类来满足我们的序列化要求。

Next we apply MyPairSerializer to our Map<MyPair, String> with the @JsonSerialize annotation. Note that we’ve only told Jackson how to serialize MyPair because it already knows how to serialize String:

接下来我们将MyPairSerializer应用于我们的Map<MyPair, String>,并使用@JsonSerialize注解。注意,我们只告诉Jackson如何序列化MyPair,因为它已经知道如何序列化String:

@JsonSerialize(keyUsing = MyPairSerializer.class) 
Map<MyPair, String> map;

Then let’s test our map serialization:

然后让我们测试一下我们的地图序列化。

map = new HashMap<>();
MyPair key = new MyPair("Abbott", "Costello");
map.put(key, "Comedy");

String jsonResult = mapper.writerWithDefaultPrettyPrinter()
  .writeValueAsString(map);

The serialized JSON output is:

串行化的JSON输出是。

{
  "Abbott and Costello" : "Comedy"
}

3.3. Map<Object, Object> Serialization

3.3.Map<Object, Object> Serialization

The most complex case is serializing a Map<Object, Object>, but most of the work is already done. Let’s use Jackson’s MapSerializer for our map, and MyPairSerializer, from the previous section, for the map’s key and value types:

最复杂的情况是序列化一个Map<Object, Object>,但是大部分工作已经完成。让我们用Jackson的MapSerializer来处理我们的地图,用上一节中的MyPairSerializer处理地图的键和值类型。

@JsonSerialize(keyUsing = MapSerializer.class)
Map<MyPair, MyPair> map;
	
@JsonSerialize(keyUsing = MyPairSerializer.class)
MyPair mapKey;

@JsonSerialize(keyUsing = MyPairSerializer.class)
MyPair mapValue;

Then let’s test out serializing our Map<MyPair, MyPair>:

然后让我们测试一下序列化我们的Map<MyPair, MyPair>

mapKey = new MyPair("Abbott", "Costello");
mapValue = new MyPair("Comedy", "1940s");
map.put(mapKey, mapValue);

String jsonResult = mapper.writerWithDefaultPrettyPrinter()
  .writeValueAsString(map);

The serialized JSON output using MyPair‘s toString() method is:

使用MyPairtoString()方法输出的序列化JSON是。

{
  "Abbott and Costello" : "Comedy and 1940s"
}

4. Deserialization

4.反序列化

Deserialization converts a stream of bytes into a Java object that we can use in code. In this section, we’ll deserialize JSON input into Maps of different signatures.

反序列化将一个字节流转换为我们可以在代码中使用的Java对象。在本节中,我们将把JSON输入反序列化为不同签名的Maps

4.1. Map<String, String> Deserialization

4.1.Map<String, String>反序列化

For a simple case, let’s take a JSON-formatted input string and convert it to a Map<String, String> Java collection:

对于一个简单的案例,让我们把一个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);

We use Jackson’s ObjectMapper, as we did for serialization, using readValue() to process the input. Also, note our use of Jackson’s TypeReference, which we’ll use in all of our deserialization examples to describe the type of our destination Map. Here is the toString() representation of our map:

我们使用Jackson的ObjectMapper,就像我们在序列化时那样,使用readValue() 来处理输入。另外,请注意我们对Jackson的TypeReference的使用,我们将在所有的反序列化示例中使用它来描述目标Map的类型。下面是我们的地图的toString() 表示。

{key=value}

4.2. Map<Object, String> Deserialization

4.2.Map<Object, String>反序列化

Now let’s change our input JSON and the TypeReference of our destination to Map<MyPair, 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);

We need to create a constructor for MyPair that takes a String with both elements and parses them to the MyPair elements:

我们需要为MyPair创建一个构造函数,它接收一个包含两个元素的String,并将它们解析为MyPair元素。

public MyPair(String both) {
    String[] pairs = both.split("and");
    this.first = pairs[0].trim();
    this.second = pairs[1].trim();
}

The toString() of our Map<MyPair,String> object is:

我们的Map<MyPair,String>对象的toString()是。

{Abbott and Costello=Comedy}

There is another option when we deserialize into a Java class that contains a Map; we can use Jackson’s KeyDeserializer class, one of the many Deserialization classes that Jackson offers. Let’s annotate our ClassWithAMap with @JsonCreator, @JsonProperty, and @JsonDeserialize:

当我们反序列化到一个包含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
}

Here we’re telling Jackson to deserialize the Map<MyPair, String> contained in ClassWithAMap, so we need to extend KeyDeserializer to describe how to deserialize the map’s key, a MyPair object, from an input String:

这里我们要告诉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);
    }
}

Then we can test the deserialization using readValue:

然后我们可以使用readValue测试反序列化。

String jsonInput = "{\"Abbott and Costello\":\"Comedy\"}";

ClassWithAMap classWithMap = mapper.readValue(jsonInput,
  ClassWithAMap.class);

Again, the toString() method of our ClassWithAMap’s map gives us the output we expect:

同样,我们的ClassWithAMap的map的toString()方法给了我们期望的输出。

{Abbott and Costello=Comedy}

4.3. Map<Object,Object> Deserialization

4.3.Map<Object,Object>反序列化

Finally, let’s change our input JSON and the TypeReference of our destination to Map<MyPair, MyPair>:

最后,让我们把我们的输入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);

The toString() of our Map<MyPair, MyPair> object is:

我们的Map<MyPair, MyPair>对象的toString()是。

{Abbott and Costello=Comedy and 1940s}

5. Conclusion

5.结论

In this brief article, we learned how to serialize and deserialize Java Maps to and from JSON-formatted Strings.

在这篇简短的文章中,我们学习了如何将JavaMaps序列化和反序列化为JSON格式的字符串。

As always, the example provided in this article is available in the GitHub repository.

一如既往,本文提供的示例可在GitHub 仓库中找到。