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

Data,Jackson
Remote
0
01:14 AM · Dec 01 ,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;
    }

    // 標準 getter 和 setter
}

3. 序列化枚舉到 JSON

3.1. 默認枚舉表示

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

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

將導致:

"MILE"

但是,當將此 枚舉序列化到 JSON 對象時,我們希望得到類似的東西:

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

3.2. 枚舉作為 JSON 對象

從 Jackson 2.1.2 版本開始,現在有一個配置選項可以處理這種表示方法。可以通過在類級別使用 @JsonFormat 註解來完成:

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

這將導致在序列化此 枚舉 以獲得 Distance.MILE 的結果:

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

3.3. 枚舉和 @JsonValue

另一種控制枚舉序列化輸出的方法是使用 @JsonValue 註解在 getter 上:

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

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

1609.34

3.4. 枚舉的自定義序列化器

如果我們在使用較早版本的 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. Deserializing JSON to Enum

First, let’s define a City class that has a Distance member:

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

Then we’ll discuss the different ways of deserializing a JSON string to an Enum.

4.1. Default Behavior

By default, Jackson will use the Enum name to deserialize from JSON.

For example, it’ll deserialize the JSON:

{"distance":"KILOMETER"}

To a Distance.KILOMETER object:

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

If we want Jackson to case-insensitively deserialize from JSON by the Enum name, we need to customize the ObjectMapper to enable the ACCEPT_CASE_INSENSITIVE_ENUMS feature.

Let’s say we have another JSON:

{"distance":"KiLoMeTeR"}

Now, let’s do a case-insensitive deserialization:

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

As the test above shows, we enable the ACCEPT_CASE_INSENSITIVE_ENUMS feature with the JsonMapper builder.

4.2. Using @JsonValue

We’ve learned how to use @JsonValue to serialize Enums. We can use the same annotation for deserialization as well. This is possible because Enum values are constants.

First, let’s use @JsonValue with one of the getter methods, getMeters():

public enum Distance {
    ...

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

The return value of the getMeters() method represents the Enum objects. Therefore, when deserializing the sample JSON:

{"distance":"0.0254"}

Jackson will look for the Enum object that has a getMeters() return value of 0.0254. In this case, the object is Distance.INCH:

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

4.3. Using @JsonProperty

The @JsonProperty annotation is used on enumeration instances:

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

By using this annotation, we’re simply telling Jackson to map the value of the @JsonProperty to the object annotated with this value.

As a result of the above declaration, the example JSON string:

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

Will be mapped to the Distance.KILOMETER object:

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

4.4. Using @JsonCreator

Jackson invokes methods annotated with @JsonCreator to get an instance of the enclosing class.

Consider the JSON representation:

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

Then we’ll define the forValues() factory method with the @JsonCreator annotation:

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;
    }

    ...
}

Note the use of the @JsonProperty annotation to bind the JSON fields with the method arguments.

Then, when we deserialize the JSON sample, we’ll get the result:

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

4.5. Using a Custom Deserializer

We can use a custom deserializer if none of the described techniques are available. For example, we might not have access to the Enum source code, or we might  be using an older Jackson version that doesn’t support one or more of the annotations covered so far.

According to our custom deserialization article, in order to deserialize the JSON provided in the previous section, we’ll start by creating the deserialization class:

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;
    }
}

Then we’ll use the @JsonDeserialize annotation on the Enum to specify our custom deserializer:

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

And our result is:

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

5. 結論

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

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

發佈 評論

Some HTML is okay.