知識庫 / JSON / Jackson RSS 訂閱

使用Jackson序列化和反序列化枚舉

Data,Jackson
HongKong
11
10:03 PM · Dec 05 ,2025

1. 概述

在本快速教程中,我們將學習如何使用 Jackson 2 控制 Java 枚舉的序列化和反序列化方式。

要更深入地瞭解並學習更多使用 Jackson 2 的技巧,請訪問主 Jackson 教程。

2. 控制枚舉的表示方式

讓我們定義以下枚舉:

public enum Distance {
    KILOMETER("km", 1000), 
    MILE("miles", 1609.34),
    METER("meters", 1), 
    INCH("inches", 0.0254),
    CENTIMETER("cm", 0.01), 
    MILLIMETER("mm", 0.001);

    private String unit;
    private final double meters;

    private Distance(String unit, double meters) {
        this.unit = unit;
        this.meters = meters;
    }

    // standard getters and setters
}

3. 枚舉序列化為 JSON

在將枚舉類型序列化為 JSON 時,需要確保枚舉值能夠正確地表示為 JSON 格式。通常,枚舉值會被轉換為其對應的數字表示,或者字符串表示(取決於具體的實現)。

以下是一些需要考慮的關鍵點:

  • 數字表示: 許多編程語言和 JSON 庫都支持將枚舉值直接轉換為其對應的數字表示。這種方法在存儲和傳輸 JSON 數據時非常有效,因為數字通常比字符串更緊湊。
  • 字符串表示: 另一種方法是使用枚舉值的字符串表示。這種方法在需要以人類可讀的方式呈現 JSON 數據時非常有用。
  • 枚舉類定義: 確保在 JSON 中正確地表示枚舉類的定義,以便在反序列化時能夠正確地創建枚舉對象。
  • JSON 庫支持: 不同的 JSON 庫對枚舉類型的支持可能有所不同。在使用 JSON 庫時,請查閲其文檔以瞭解如何正確地處理枚舉類型。
// 示例:
// 假設有一個枚舉類型:
// enum Color {
//   Red,
//   Green,
//   Blue
// }

// 將枚舉序列化為 JSON:
// const colorJson = {
//   color: "Red" // 或者 color: 0 (取決於實現)
// };

3.1. 默認枚舉表示形式

默認情況下,Jackson會將Java枚舉表示為簡單的字符串。例如:

new ObjectMapper().writeValueAsString(Distance.MILE);

將會產生以下結果:

"MILE"

然而,在將此 枚舉類型 (Enum) 轉換為 JSON 對象時,我們希望得到類似的結果:

{"unit":"miles","meters":1609.34}

3.2. 枚舉作為 JSON 對象

從 Jackson 2.1.2 版本開始,現在有配置選項可以處理這種表示形式。可以通過在類級別使用 <em @JsonFormat</em>> 註解來實現。

@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum Distance { ... }

這將導致當序列化此 枚舉 Distance 中的 MILE 時,獲得所需的結果:

{"unit":"miles","meters":1609.34}

3.3. 枚舉和 <em @JsonValue

使用 <em @JsonValue 註解,可以輕鬆控制枚舉類型的序列化輸出。

public enum Distance { 
    ...
 
    @JsonValue
    public String getMeters() {
        return meters;
    }
}

我們表達的是,getMeters() 實際上是這個枚舉的表示形式。因此,序列化的結果將是:

1609.34

3.4. 自定義枚舉序列化器

如果使用早於 2.1.2 版本的 Jackson,或者需要對枚舉進行更高級的自定義,我們可以使用 自定義 Jackson 序列化器。首先,我們需要定義它:

public class DistanceSerializer extends StdSerializer {
    
    public DistanceSerializer() {
        super(Distance.class);
    }

    public DistanceSerializer(Class t) {
        super(t);
    }

    public void serialize(
      Distance distance, JsonGenerator generator, SerializerProvider provider) 
      throws IOException, JsonProcessingException {
        generator.writeStartObject();
        generator.writeFieldName("name");
        generator.writeString(distance.name());
        generator.writeFieldName("unit");
        generator.writeString(distance.getUnit());
        generator.writeFieldName("meters");
        generator.writeNumber(distance.getMeters());
        generator.writeEndObject();
    }
}

然後我們可以將序列化器應用於將被序列化的類:

@JsonSerialize(using = DistanceSerializer.class)
public enum TypeEnum { ... }

這會導致以下結果:

{"name":"MILE","unit":"miles","meters":1609.34}

4. 將 JSON 反序列化為枚舉

首先,讓我們定義一個 City 類,該類包含一個 Distance 成員:

public class City {
    
    private Distance distance;
    ...    
}

然後我們將會討論如何將 JSON 字符串反序列化為枚舉。

4.1. 默認行為

默認情況下,Jackson 會使用枚舉的名稱進行從 JSON 中反序列化。

例如,它會反序列化以下 JSON:

{"distance":"KILOMETER"}

向一個 Distance.KILOMETER 對象:

City city = new ObjectMapper().readValue(json, City.class);
assertEquals(Distance.KILOMETER, city.getDistance());

如果想要Jackson在枚舉名稱下進行大小寫不敏感的JSON反序列化,我們需要自定義 ObjectMapper 以啓用 ACCEPT_CASE_INSENSITIVE_ENUMS 功能

假設我們還有一個JSON:

{"distance":"KiLoMeTeR"}

現在,我們來做一個不區分大小寫的反序列化:

ObjectMapper objectMapper = JsonMapper.builder()
  .enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS)
  .build();
City city = objectMapper.readValue(json, City.class);
                                                     
assertEquals(Distance.KILOMETER, city.getDistance());

如上測試所示,我們使用 ACCEPT_CASE_INSENSITIVE_ENUMS 功能,並通過 JsonMapper 構建器啓用它。

4.2. 使用 @JsonValue 標註

我們已經學習瞭如何使用 @JsonValue 標註枚舉進行序列化。 同樣,我們也可以使用相同的標註進行反序列化。 這是因為枚舉值是常量。

首先,讓我們使用 @JsonValue 標註一個獲取器方法,getMeters()

public enum Distance {
    ...

    @JsonValue
    public double getMeters() {
        return meters;
    }
}

getMeters() 方法的返回值代表枚舉對象。因此,在反序列化示例文本 JSON 時:

{"distance":"0.0254"}

Jackson 將查找具有 getMeters() 返回值為 0.0254 的枚舉對象。在這種情況下,該對象是 Distance.英寸:

assertEquals(Distance.INCH, city.getDistance());

4.3. 使用 @JsonProperty

@JsonProperty 註解用於枚舉實例。

public enum Distance {
    @JsonProperty("distance-in-km")
    KILOMETER("km", 1000), 
    @JsonProperty("distance-in-miles")
    MILE("miles", 1609.34);
 
    ...
}

通過使用此註解,我們只是告訴 Jackson 將 @JsonProperty 的值映射到帶有此值的對象

由於上述聲明,示例 JSON 字符串:

{"distance": "distance-in-km"}

將被映射到 Distance.KILOMETER 對象:

assertEquals(Distance.KILOMETER, city.getDistance());

4.4. 使用 @JsonCreator

Jackson 通過調用帶有 @JsonCreator 註解的方法來獲取包含類的一個實例。

請考慮以下 JSON 表示:

{
    "distance": {
        "unit":"miles", 
        "meters":1609.34
    }
}

然後,我們將定義帶有 forValues() 工廠方法的 @JsonCreator 註解:

public enum Distance {
   
    @JsonCreator
    public static Distance forValues(@JsonProperty("unit") String unit,
      @JsonProperty("meters") double meters) {
        for (Distance distance : Distance.values()) {
            if (
              distance.unit.equals(unit) && Double.compare(distance.meters, meters) == 0) {
                return distance;
            }
        }

        return null;
    }

    ...
}

請注意使用 @JsonProperty 註解來將 JSON 字段與方法參數關聯。

然後,當我們反序列化 JSON 樣本時,我們會得到結果:

assertEquals(Distance.MILE, city.getDistance());

4.5. 使用自定義反序列化器

如果以上技術方法均不可用,我們可以使用自定義的反序列化器。例如,我們可能無法訪問枚舉的源代碼,或者我們可能在使用舊版本的 Jackson,該版本不支持之前介紹的一或多個註解。

根據我們自定義反序列化文章所述,為了反序列化上一節中提供的 JSON 數據,我們首先將創建反序列化類:

public class CustomEnumDeserializer extends StdDeserializer<Distance> {

    @Override
    public Distance deserialize(JsonParser jsonParser, DeserializationContext ctxt)
      throws IOException, JsonProcessingException {
        JsonNode node = jsonParser.getCodec().readTree(jsonParser);

        String unit = node.get("unit").asText();
        double meters = node.get("meters").asDouble();

        for (Distance distance : Distance.values()) {
           
            if (distance.getUnit().equals(unit) && Double.compare(
              distance.getMeters(), meters) == 0) {
                return distance;
            }
        }

        return null;
    }
}

然後,我們將使用 @JsonDeserialize 標註應用於枚舉,以指定我們的自定義反序列器:

@JsonDeserialize(using = CustomEnumDeserializer.class)
public enum Distance {
   ...
}

結果如下:

我們的結果是:

assertEquals(Distance.MILE, city.getDistance());

5. 結論

本文闡述瞭如何更好地控制 Java 枚舉的序列化和反序列化過程及格式。

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

發佈 評論

Some HTML is okay.