知識庫 / JSON / Jackson RSS 訂閱

Jackson序列化與反序列化

Data,Jackson
HongKong
10
09:54 PM · Dec 05 ,2025

1. 概述

本快速教程將介紹如何使用 Jackson 對 Java 映射數據進行序列化和反序列化。

我們將演示如何將 Map<String, String>, Map<Object, String>Map<Object, Object> 序列化為 JSON 格式的字符串,以及反序列化為這些映射數據。

2. Maven 配置

...

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

我們可以在 這裏 獲取 Jackson 的最新版本。

3. 序列化

序列化將 Java 對象轉換為字節流,以便可以持久化存儲或根據需要進行共享。Java 中的 Maps 是一個將鍵 Object 映射到值 Object 的集合,並且通常是序列化時最不直觀的對象。

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,並使用 toString() 方法將它寫為格式良好的 JSON String

{
  "key" : "value"
}

3.2. Map<Object, String> 的序列化

通過幾個額外的步驟,我們還可以序列化包含自定義 Java 類的 Map。 讓我們創建一個 MyPair</em/> 類來表示兩個相關的 String</em/> 對象。

注意:獲取器和設置器應為 public,並且我們應該使用 @JsonValue</em/> 註解 toString()</em/> 方法,以確保 Jackson 在序列化時使用此自定義 toString()</em/> 方法。

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,正如其名稱所示,使用 MyPairtoString() 方法,將 MyPair 序列化為 JSON。 此外,Jackson 提供了許多 Serializer 類,以滿足我們的序列化需求。

接下來,我們使用 MyPairSerializer 對我們的 Map<MyPair, String> 應用該類,並使用 @JsonSerialize 註解。 請注意,我們僅告訴 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);

使用 MyPairtoString() 方法序列化的 JSON 輸出如下:

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

3.4. @JsonKey 註解

當創建 Map 時,一個對象可以是鍵或值。 此外,當對象作為 Map 中的鍵出現與作為值出現時,我們可能需要不同的序列化策略。 因此,讓我們學習如何使用 @JsonKey 註解來實現這一點。

讓我們首先定義 Fruit 類,該類具有兩個成員,即 varietyname

public class Fruit {
    public String variety;

    @JsonKey
    public String name;

    public Fruit(String variety, String name) {
        this.variety = variety;
        this.name = name;
    }

    @JsonValue
    public String getFullName() {
        return this.variety + " " + this.name;
    }
}

我們必須注意到,在任何 Fruit 對象作為 Map 鍵出現時,我們都希望使用它的名稱進行序列化。但是,當它作為值出現時,我們希望同時使用它的名稱和品種。

現在,讓我們初始化兩個 Fruit 類的實例和一個 ObjectMapper 類的實例,用於序列化目的:

private static final Fruit FRUIT1 = new Fruit("Alphonso", "Mango");
private static final Fruit FRUIT2 = new Fruit("Black", "Grapes");
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

接下來,我們應該記住,在序列化獨立實例時,會使用 @JsonValue 屬性進行序列化。讓我們驗證一下這對於這兩個對象是否成立:

@Test
public void givenObject_WhenSerialize_ThenUseJsonValueForSerialization() 
  throws JsonProcessingException {
    String serializedValueForFruit1 = OBJECT_MAPPER.writeValueAsString(FRUIT1);
    Assertions.assertEquals("\"Alphonso Mango\"", serializedValueForFruit1);
    String serializedValueForFruit2 = OBJECT_MAPPER.writeValueAsString(FRUIT2);
    Assertions.assertEquals("\"Black Grapes\"", serializedValueForFruit2);
}

進一步來説,讓我們將 selectionByFruit Map,該 Map 包含 Fruit 類的實例,作為鍵進行序列化:

@Test
public void givenMapWithObjectKeys_WhenSerialize_ThenUseJsonKeyForSerialization() 
  throws JsonProcessingException {
    // Given
    Map<Fruit, String> selectionByFruit = new LinkedHashMap<>();
    selectionByFruit.put(FRUIT1, "Hagrid");
    selectionByFruit.put(FRUIT2, "Hercules");
    // When
    String serializedValue = OBJECT_MAPPER.writeValueAsString(selectionByFruit);
    // Then
    Assertions.assertEquals("{\"Mango\":\"Hagrid\",\"Grapes\":\"Hercules\"}", serializedValue);
}

正如預期的那樣,@JsonKey 註解被用於此序列化。此外,我們使用了 LinkedHashMap 以在訪問和序列化期間修復鍵的順序。

最後,讓我們查看將 selectionByPerson Map(其中包含 Fruit 類的實例)序列化後的結果:

@Test
public void givenMapWithObjectValues_WhenSerialize_ThenUseJsonValueForSerialization() 
  throws JsonProcessingException {
    // Given
    Map<String, Fruit> selectionByPerson = new LinkedHashMap<>();
    selectionByPerson.put("Hagrid", FRUIT1);
    selectionByPerson.put("Hercules", FRUIT2);
    // When
    String serializedValue = OBJECT_MAPPER.writeValueAsString(selectionByPerson);
    // Then
    Assertions.assertEquals("{\"Hagrid\":\"Alphonso Mango\",\"Hercules\":\"Black Grapes\"}", 
      serializedValue);
}

太棒了!我們已成功地根據對象在 Map 中作為鍵或值的角色,切換了序列化策略。

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() 表示形式的 Map

{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 將 Map<MyPair, String> ,它包含在 ClassWithAMap 中,進行反序列化。因此,我們需要擴展 KeyDeserializer 以描述如何從輸入 String 中反序列化 map 的鍵,即 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 的 map 的 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}

結論

在本文中,我們學習瞭如何將 Java 中的 Maps 序列化和反序列化為 JSON 格式的字符串。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.